The Pedigree Project 0.1
event-descriptor-contract-test/clock.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <pthread.h>
4#include <string.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/timerfd.h>
9
10static int restore(int64_t realtime, int64_t monotonic) {
11 struct timespec target = ed_timespec(realtime + ed_now(CLOCK_MONOTONIC) - monotonic);
12 return clock_settime(CLOCK_REALTIME, &target);
13}
14static int jump(int64_t delta) {
15 struct timespec target = ed_timespec(ed_now(CLOCK_REALTIME) + delta);
16 return clock_settime(CLOCK_REALTIME, &target);
17}
18static int64_t remaining(const struct itimerspec* setting) {
19 return (int64_t)setting->it_value.tv_sec * 1000000000 + setting->it_value.tv_nsec;
20}
21
22static int deadlines(void) {
23 int failed = 0, absolute = -1, relative = -1, monotonic_fd = -1, active = 0;
24 pthread_t worker;
25 struct ed_reader reader = {.size = 8};
26 struct itimerspec setting = {0}, snapshot;
27 uint64_t count;
28 const int64_t realtime = ed_now(CLOCK_REALTIME), monotonic = ed_now(CLOCK_MONOTONIC);
29 CHECK((absolute = timerfd_create(CLOCK_REALTIME, 0)) >= 0);
30 CHECK((relative = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK)) >= 0);
31 CHECK((monotonic_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK)) >= 0);
32 setting.it_value = ed_timespec(ed_now(CLOCK_REALTIME) + 5000000000);
33 CHECK(timerfd_settime(absolute, TFD_TIMER_ABSTIME, &setting, NULL) == 0);
34 setting.it_value = ed_timespec(3000000000);
35 CHECK(timerfd_settime(relative, TFD_TIMER_CANCEL_ON_SET, &setting, NULL) == 0);
36 setting.it_value = ed_timespec(ed_now(CLOCK_MONOTONIC) + 3000000000);
37 CHECK(timerfd_settime(monotonic_fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &setting,
38 NULL) == 0);
39 reader.fd = absolute;
40 CHECK(pthread_create(&worker, NULL, ed_read_thread, &reader) == 0);
41 active = 1;
42 CHECK(ed_wait(&reader.started, 1000) == 0);
43 ed_pause(30);
44 CHECK(!__atomic_load_n(&reader.done, __ATOMIC_ACQUIRE));
45 const int64_t jumped = ed_now(CLOCK_MONOTONIC);
46 CHECK(jump(10000000000) == 0);
47 CHECK(ed_wait(&reader.done, 1000) == 0 && pthread_join(worker, NULL) == 0);
48 active = 0;
49 memcpy(&count, reader.bytes, sizeof(count));
50 CHECK(reader.result == 8 && count == 1 && reader.finished - jumped >= 0 &&
51 reader.finished - jumped < 500000000);
52 CHECK(timerfd_gettime(relative, &snapshot) == 0 && remaining(&snapshot) > 1000000000);
53 CHECK(timerfd_gettime(monotonic_fd, &snapshot) == 0 && remaining(&snapshot) > 1000000000);
54 CHECK(read(relative, &count, 8) == -1 && errno == EAGAIN);
55 CHECK(read(monotonic_fd, &count, 8) == -1 && errno == EAGAIN);
56
57 CHECK(fcntl(absolute, F_SETFL, O_NONBLOCK) == 0);
58 setting.it_value = ed_timespec(ed_now(CLOCK_REALTIME) + 300000000);
59 CHECK(timerfd_settime(absolute, TFD_TIMER_ABSTIME, &setting, NULL) == 0);
60 CHECK(jump(-1000000000) == 0);
61 ed_pause(400);
62 CHECK(read(absolute, &count, 8) == -1 && errno == EAGAIN);
63 CHECK(jump(1000000000) == 0);
64 CHECK(ed_readable(absolute, 1000) == 1 && read(absolute, &count, 8) == 8 && count == 1);
65out:
66 if (absolute >= 0)
67 close(absolute);
68 if (active) {
69 pthread_cancel(worker);
70 pthread_join(worker, NULL);
71 }
72 if (relative >= 0)
73 close(relative);
74 if (monotonic_fd >= 0)
75 close(monotonic_fd);
76 if (restore(realtime, monotonic))
77 failed = 1;
78 return failed;
79}
80
81static int cancellation(void) {
82 int failed = 0, fd = -1, alias = -1, active = 0;
83 pthread_t worker;
84 struct ed_reader reader = {.size = 8};
85 const int flags = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;
86 struct itimerspec setting = {0}, snapshot;
87 uint64_t count;
88 const int64_t realtime = ed_now(CLOCK_REALTIME), monotonic = ed_now(CLOCK_MONOTONIC);
89 CHECK((fd = timerfd_create(CLOCK_REALTIME, 0)) >= 0 && (alias = dup(fd)) >= 0);
90 setting.it_value = ed_timespec(ed_now(CLOCK_REALTIME) + 5000000000);
91 CHECK(timerfd_settime(fd, flags, &setting, NULL) == 0);
92 reader.fd = fd;
93 CHECK(pthread_create(&worker, NULL, ed_read_thread, &reader) == 0);
94 active = 1;
95 CHECK(ed_wait(&reader.started, 1000) == 0);
96 ed_pause(30);
97 CHECK(!__atomic_load_n(&reader.done, __ATOMIC_ACQUIRE));
98 const int64_t jumped = ed_now(CLOCK_MONOTONIC);
99 CHECK(jump(1000000000) == 0);
100 CHECK(ed_wait(&reader.done, 1000) == 0 && pthread_join(worker, NULL) == 0);
101 active = 0;
102 CHECK(reader.result == -1 && reader.error == ECANCELED && reader.finished - jumped >= 0 &&
103 reader.finished - jumped < 500000000);
104 CHECK(fcntl(alias, F_SETFL, O_NONBLOCK) == 0);
105 CHECK(read(alias, &count, 8) == -1 && errno == EAGAIN);
106
107 setting.it_value = ed_timespec(ed_now(CLOCK_REALTIME) + 5000000000);
108 CHECK(timerfd_settime(alias, flags, &setting, NULL) == 0);
109 CHECK(jump(-1000000000) == 0 && ed_readable(fd, 0) == 1);
110 setting.it_value = ed_timespec(ed_now(CLOCK_REALTIME) + 300000000);
111 CHECK(timerfd_settime(fd, flags, &setting, NULL) == -1 && errno == ECANCELED);
112 CHECK(timerfd_gettime(alias, &snapshot) == 0 && remaining(&snapshot) > 0);
113 CHECK(ed_readable(alias, 2000) == 1 && read(alias, &count, 8) == 8 && count == 1);
114 CHECK(read(fd, &count, 8) == -1 && errno == EAGAIN);
115
116 setting.it_value = (struct timespec){0};
117 setting.it_interval = ed_timespec(77000000);
118 CHECK(timerfd_settime(alias, flags, &setting, NULL) == 0);
119 CHECK(timerfd_gettime(fd, &snapshot) == 0 && remaining(&snapshot) == 0 &&
120 snapshot.it_interval.tv_nsec == 77000000);
121 CHECK(jump(1000000000) == 0 && ed_readable(fd, 0) == 1);
122 CHECK(read(alias, &count, 8) == -1 && errno == ECANCELED);
123 CHECK(read(fd, &count, 8) == -1 && errno == EAGAIN);
124out:
125 if (fd >= 0)
126 close(fd);
127 if (alias >= 0)
128 close(alias);
129 if (active) {
130 pthread_cancel(worker);
131 pthread_join(worker, NULL);
132 }
133 if (restore(realtime, monotonic))
134 failed = 1;
135 return failed;
136}
137
138int event_descriptor_test_clock(void) {
139 if (geteuid() != 0) {
140 puts("EVENT-DESCRIPTOR-CONTRACT: SKIP clock requires root");
141 return 0;
142 }
143 return deadlines() || cancellation();
144}
Definition waits.c:9