13#include <sys/syscall.h>
16enum { Wait = 128, Wake = 129, Requeue = 131, Bitset = 137, Realtime = 256 };
20 fprintf(stderr, "FUTEX-CLOCK: line=%d errno=%d check=%s\n", __LINE__, errno, #x); \
25static int64_t now(clockid_t clock) {
26 struct timespec value;
27 return syscall(SYS_clock_gettime, clock, &value)
29 : (int64_t)value.tv_sec * 1000000000 + value.tv_nsec;
31static struct timespec timespec(int64_t value) {
32 return (
struct timespec){value / 1000000000, value % 1000000000};
34static void pause_ms(
int milliseconds) {
35 struct timespec duration = timespec((int64_t)milliseconds * 1000000);
36 while (nanosleep(&duration, &duration) && errno == EINTR) {
39static int step(int64_t delta) {
40 struct timespec target = timespec(now(CLOCK_REALTIME) + delta);
41 return clock_settime(CLOCK_REALTIME, &target);
43static int wake(
int* address) {
44 return syscall(SYS_futex, address, Wake, INT_MAX, NULL, NULL, 0);
46static int requeue(
int* source,
int* destination) {
47 return syscall(SYS_futex, source, Requeue, 0, 1, destination, 0);
52 int operation, timed, result, error;
53 struct timespec deadline;
54 int64_t started, finished;
57static void* wait_thread(
void* argument) {
58 struct waiter* wait = argument;
59 wait->started = now(CLOCK_MONOTONIC);
60 wait->result = syscall(SYS_futex, wait->address, wait->operation, 0,
61 wait->timed ? &wait->deadline : NULL, NULL, UINT32_MAX);
63 wait->finished = now(CLOCK_MONOTONIC);
64 __atomic_store_n(&wait->done, 1, __ATOMIC_RELEASE);
67static int completed(
struct waiter* wait,
int milliseconds) {
68 const int64_t until = now(CLOCK_MONOTONIC) + (int64_t)milliseconds * 1000000;
69 while (!__atomic_load_n(&wait->done, __ATOMIC_ACQUIRE)) {
70 if (now(CLOCK_MONOTONIC) >= until)
76static int enrolled(
struct waiter* wait,
int* destination) {
77 const int64_t until = now(CLOCK_MONOTONIC) + 2000000000;
78 while (!__atomic_load_n(&wait->done, __ATOMIC_ACQUIRE) && now(CLOCK_MONOTONIC) < until) {
79 int moved = requeue(wait->address, destination);
87static int validation(
void) {
89 struct timespec past = {0}, invalid = {0, 1000000000};
90 CHECK(syscall(SYS_futex, &word, Bitset | Realtime, 1, &past, NULL, UINT32_MAX) == -1 &&
92 CHECK(syscall(SYS_futex, &word, Bitset | Realtime, 0, &past, NULL, UINT32_MAX) == -1 &&
94 CHECK(syscall(SYS_futex, &word, Bitset, 0, &past, NULL, UINT32_MAX) == -1 && errno == ETIMEDOUT);
95 CHECK(syscall(SYS_futex, &word, Wait, 0, &past, NULL, 0) == -1 && errno == ETIMEDOUT);
96 CHECK(syscall(SYS_futex, &word, Bitset, 0, &invalid, NULL, UINT32_MAX) == -1 && errno == EINVAL);
97 invalid = (
struct timespec){-1, 0};
98 CHECK(syscall(SYS_futex, &word, Bitset, 0, &invalid, NULL, UINT32_MAX) == -1 && errno == EINVAL);
99 CHECK(syscall(SYS_futex, &word, Bitset, 0, (
void*)-1, NULL, UINT32_MAX) == -1 && errno == EFAULT);
100 CHECK(syscall(SYS_futex, (
char*)&word + 1, Bitset, 0, NULL, NULL, UINT32_MAX) == -1 &&
102 CHECK(syscall(SYS_futex, &word, Bitset, 0, NULL, NULL, 0) == -1 && errno == EINVAL);
103 CHECK(syscall(SYS_futex, &word, Bitset, 0, NULL, NULL, 1) == -1 && errno == ENOSYS);
104 CHECK(syscall(SYS_futex, &word, Wake | Realtime, 1, NULL, NULL, 0) == -1 && errno == ENOSYS);
105 CHECK(wake(&word) == 0);
109static int ordinary_and_indefinite(
void) {
111 const int operations[] = {Wait, Bitset, Bitset | Realtime};
112 for (
unsigned n = 0; n <
sizeof(operations) /
sizeof(operations[0]); ++n) {
114 struct waiter wait = {.address = &word, .operation = operations[n]};
115 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0);
116 CHECK(enrolled(&wait, &word));
117 CHECK(wake(&word) == 1 && completed(&wait, 2000));
118 CHECK(pthread_join(thread, NULL) == 0 && wait.result == 0);
119 CHECK(wake(&word) == 0);
124static int deadlines(
void) {
126 for (
int realtime = 0; realtime <= 1; ++realtime) {
128 .address = &word, .operation = Bitset | (realtime ? Realtime : 0), .timed = 1};
129 const int64_t before = now(CLOCK_MONOTONIC);
130 wait.deadline = timespec(now(realtime ? CLOCK_REALTIME : CLOCK_MONOTONIC) + 200000000);
132 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0);
133 CHECK(completed(&wait, 3000) && pthread_join(thread, NULL) == 0);
134 CHECK(wait.result == -1 && wait.error == ETIMEDOUT && wait.finished - before >= 200000000);
135 CHECK(wake(&word) == 0);
140static int clock_steps(
void) {
143 struct waiter wait = {.address = &word, .operation = Bitset | Realtime, .timed = 1};
144 wait.deadline = timespec(now(CLOCK_REALTIME) + 10000000000);
145 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0 && enrolled(&wait, &word));
146 CHECK(step(20000000000) == 0);
147 CHECK(completed(&wait, 2000) && pthread_join(thread, NULL) == 0);
148 CHECK(wait.result == -1 && wait.error == ETIMEDOUT && wake(&word) == 0);
150 wait = (
struct waiter){.address = &word, .operation = Bitset | Realtime, .timed = 1};
151 wait.deadline = timespec(now(CLOCK_REALTIME) + 1000000000);
152 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0 && enrolled(&wait, &word));
153 CHECK(step(-5000000000) == 0);
155 CHECK(!__atomic_load_n(&wait.done, __ATOMIC_ACQUIRE));
156 CHECK(step(10000000000) == 0);
157 CHECK(completed(&wait, 2000) && pthread_join(thread, NULL) == 0);
158 CHECK(wait.result == -1 && wait.error == ETIMEDOUT && wake(&word) == 0);
162static int requeue_clock_change(
void) {
163 int source = 0, destination = 0;
164 pthread_t threads[2];
165 struct waiter waits[2] = {{.address = &source, .operation = Wait},
166 {.address = &source, .operation = Bitset | Realtime, .timed = 1}};
167 waits[1].deadline = timespec(now(CLOCK_REALTIME) + 10000000000);
168 for (
int n = 0; n < 2; ++n) {
169 CHECK(pthread_create(&threads[n], NULL, wait_thread, &waits[n]) == 0);
170 CHECK(enrolled(&waits[n], &destination));
172 __atomic_store_n(&source, 1, __ATOMIC_RELEASE);
173 CHECK(step(-1000000000) == 0);
175 CHECK(wake(&source) == 0 && wake(&destination) == 2);
176 for (
int n = 0; n < 2; ++n)
177 CHECK(completed(&waits[n], 2000) && pthread_join(threads[n], NULL) == 0 &&
178 waits[n].result == 0);
179 CHECK(wake(&destination) == 0);
183static volatile sig_atomic_t signals;
184static void handler(
int signal) {
188static int interruption(
void) {
190 struct sigaction action = {.sa_handler = handler};
191 sigemptyset(&action.sa_mask);
192 CHECK(sigaction(SIGUSR1, &action, NULL) == 0);
193 for (
int n = 0; n < 4; ++n) {
195 struct waiter wait = {.address = &word, .operation = Bitset | Realtime, .timed = 1};
196 wait.deadline = timespec(now(CLOCK_REALTIME) + 10000000000);
197 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0 && enrolled(&wait, &word));
198 CHECK(pthread_kill(thread, SIGUSR1) == 0);
199 CHECK(completed(&wait, 2000) && pthread_join(thread, NULL) == 0);
200 CHECK(wait.result == -1 && wait.error == EINTR && wake(&word) == 0);
207static int timeout_wake_race(
void) {
209 for (
int n = 0; n < 24; ++n) {
211 struct waiter wait = {.address = &word, .operation = Bitset, .timed = 1};
212 wait.deadline = timespec(now(CLOCK_MONOTONIC) + 10000000);
213 CHECK(pthread_create(&thread, NULL, wait_thread, &wait) == 0);
215 const int woken = wake(&word);
216 CHECK(completed(&wait, 2000) && pthread_join(thread, NULL) == 0);
217 CHECK((woken == 1 && wait.result == 0) ||
218 (woken == 0 && wait.result == -1 && wait.error == ETIMEDOUT));
223static int terminate_wait(
void) {
225 CHECK(pipe(ready) == 0);
226 pid_t child = fork();
233 struct waiter wait = {.address = &word, .operation = Bitset | Realtime, .timed = 1};
234 wait.deadline = timespec(now(CLOCK_REALTIME) + 10000000000);
235 if (pthread_create(&thread, NULL, wait_thread, &wait) || !enrolled(&wait, &word) ||
236 write(ready[1],
"r", 1) != 1)
243 int valid = read(ready[0], &token, 1) == 1 && token ==
'r';
246 CHECK(kill(child, SIGKILL) == 0 && waitpid(child, &status, 0) == child);
247 CHECK(valid && WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL);
253 setvbuf(stdout, NULL, _IONBF, 0);
254 if (geteuid() != 0) {
255 fputs(
"FUTEX-CLOCK: FAIL requires root for clock-step contracts\n", stderr);
261 } tests[] = {{
"validation", validation},
262 {
"ordinary-and-indefinite", ordinary_and_indefinite},
263 {
"deadlines", deadlines},
264 {
"clock-steps", clock_steps},
265 {
"requeue-clock-change", requeue_clock_change},
266 {
"interruption", interruption},
267 {
"timeout-wake-race", timeout_wake_race},
268 {
"terminate-wait", terminate_wait}};
269 for (
unsigned n = 0; n <
sizeof(tests) /
sizeof(tests[0]); ++n) {
270 printf(
"FUTEX-CLOCK: RUN %s\n", tests[n].name);
271 const int64_t realtime = now(CLOCK_REALTIME), monotonic = now(CLOCK_MONOTONIC);
272 pid_t child = fork();
275 _exit(tests[n].test() ? 1 : 0);
280 reaped = waitpid(child, &status, 0);
281 }
while (reaped < 0 && errno == EINTR);
282 struct timespec restored = timespec(realtime + now(CLOCK_MONOTONIC) - monotonic);
283 int failed = child < 0 || reaped != child || !WIFEXITED(status) || WEXITSTATUS(status);
284 if (clock_settime(CLOCK_REALTIME, &restored))
286 printf(
"FUTEX-CLOCK: %s %s status=%d\n", failed ?
"FAIL" :
"PASS", tests[n].name, status);
290 puts(
"FUTEX-CLOCK: END PASS");