xref: /linux/tools/testing/vsock/timeout.c (revision 1b98f357dadd6ea613a435fbaef1a5dd7b35fd21)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Timeout API for single-threaded programs that use blocking
3  * syscalls (read/write/send/recv/connect/accept).
4  *
5  * Copyright (C) 2017 Red Hat, Inc.
6  *
7  * Author: Stefan Hajnoczi <stefanha@redhat.com>
8  */
9 
10 /* Use the following pattern:
11  *
12  *   timeout_begin(TIMEOUT);
13  *   do {
14  *       ret = accept(...);
15  *       timeout_check("accept");
16  *   } while (ret < 0 && ret == EINTR);
17  *   timeout_end();
18  */
19 
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <time.h>
25 #include "timeout.h"
26 
27 static volatile bool timeout;
28 
29 /* SIGALRM handler function.  Do not use sleep(2), alarm(2), or
30  * setitimer(2) while using this API - they may interfere with each
31  * other.
32  *
33  * If you need to sleep, please use timeout_sleep() provided by this API.
34  */
35 void sigalrm(int signo)
36 {
37 	timeout = true;
38 }
39 
40 /* Start a timeout.  Call timeout_check() to verify that the timeout hasn't
41  * expired.  timeout_end() must be called to stop the timeout.  Timeouts cannot
42  * be nested.
43  */
44 void timeout_begin(unsigned int seconds)
45 {
46 	alarm(seconds);
47 }
48 
49 /* Exit with an error message if the timeout has expired */
50 void timeout_check(const char *operation)
51 {
52 	if (timeout) {
53 		fprintf(stderr, "%s timed out\n", operation);
54 		exit(EXIT_FAILURE);
55 	}
56 }
57 
58 /* Stop a timeout */
59 void timeout_end(void)
60 {
61 	alarm(0);
62 	timeout = false;
63 }
64 
65 /* Sleep in a timeout section.
66  *
67  * nanosleep(2) can be used with this API since POSIX.1 explicitly
68  * specifies that it does not interact with signals.
69  */
70 int timeout_usleep(useconds_t usec)
71 {
72 	struct timespec ts = {
73 		.tv_sec = usec / 1000000,
74 		.tv_nsec = (usec % 1000000) * 1000,
75 	};
76 
77 	return nanosleep(&ts, NULL);
78 }
79