-
Notifications
You must be signed in to change notification settings - Fork 0
/
timers.c
156 lines (128 loc) · 3.09 KB
/
timers.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h>
typedef void (*work_t)(void);
pid_t waitpid_eintr(int *status);
void watchdog_worker_timer(work_t work, long long timeout);
void handle_alarm_signal(int signal, siginfo_t *info, void *context);
void setup_timer(long long timeout);
void system_timers(work_t work, long long timeout);
void endless_loop(void);
void short_task(void);
int main() {
system_timers(endless_loop, 1000);
system_timers(short_task, 1000);
watchdog_worker_timer(endless_loop, 1000);
watchdog_worker_timer(short_task, 1000);
return 0;
}
pid_t waitpid_eintr(int *status) {
pid_t pid = 0;
while ( (pid = waitpid(WAIT_ANY, status, 0)) == -1 ) {
if (errno == EINTR) {
continue;
} else {
perror("waitpid");
abort();
}
}
return pid;
}
void watchdog_worker_timer(work_t work, long long timeout) {
const pid_t timer_pid = fork();
if (timer_pid == -1) {
perror("fork timer");
abort();
}
if (timer_pid == 0) {
/// Timer process
usleep(timeout * 1000);
exit(0);
}
const pid_t worker_pid = fork();
if (worker_pid == -1) {
perror("fork worker");
abort();
}
if (worker_pid == 0) {
/// Worker process
work();
exit(0);
}
int status = 0;
const pid_t finished_first = waitpid_eintr(&status);
if (finished_first == timer_pid) {
printf("timed out\n");
kill(worker_pid, SIGKILL);
} else if (finished_first == worker_pid) {
printf("all good\n");
kill(timer_pid, SIGKILL);
} else {
assert(0 && "Something went wrong");
}
waitpid_eintr(&status);
}
void handle_alarm_signal(int signal, siginfo_t *info, void *context) {
_exit(112);
}
void setup_timer(long long timeout) {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_sigaction = &handle_alarm_signal;
if (sigaction(SIGALRM, &action, NULL) != 0) {
perror("sigaction");
abort();
}
struct itimerval timer;
timer.it_value.tv_sec = timeout / 1000;
/// Cut off seconds, and convert what's left into microseconds
timer.it_value.tv_usec = (timeout % 1000) * 1000;
/// Do not repeat
timer.it_interval.tv_sec = 0;
timer.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &timer, NULL) != 0) {
perror("setitimer");
abort();
}
}
void system_timers(work_t work, long long timeout) {
const pid_t worker_pid = fork();
if (worker_pid == -1) {
perror("fork worker");
abort();
}
if (worker_pid == 0) {
setup_timer(timeout);
work();
exit(0);
}
int status = 0;
waitpid_eintr(&status);
if (WIFEXITED(status) && WEXITSTATUS(status) == 112) {
printf("timed out\n");
}
else if (WIFEXITED(status) && WEXITSTATUS(status) != 144) {
printf("all good\n");
}
}
void endless_loop(void) {
printf("starting endless loop\n");
/// Avoiding UB
volatile int x = 0;
while (1) {
if (x != 0) {
break;
}
}
}
void short_task(void) {
printf("finished short task\n");
}