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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
|
#define _GNU_SOURCE
#include <errno.h>
#include <poll.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/select.h>
#include <sys/timerfd.h>
#include <sys/utsname.h>
#include <time.h>
#include <unistd.h>
static int fails;
#define CHECK(cond, ...) \
do { \
if (!(cond)) { \
fails++; \
printf("timerfdtest: FAIL "); \
printf(__VA_ARGS__); \
printf("\n"); \
} \
} while (0)
static long long now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000LL + ts.tv_nsec / 1000000;
}
static void ms_sleep(long ms)
{
struct timespec ts = { ms / 1000, ms % 1000 * 1000000L };
nanosleep(&ts, NULL);
}
static int arm(int fd, long ms, long every_ms)
{
struct itimerspec its = { { every_ms / 1000, every_ms % 1000 * 1000000L }, { ms / 1000, ms % 1000 * 1000000L } };
return timerfd_settime(fd, 0, &its, NULL);
}
static void one_shot(void)
{
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
CHECK(fd >= 0, "timerfd_create: errno %d", errno);
long long t0 = now_ms();
CHECK(arm(fd, 150, 0) == 0, "settime: errno %d", errno);
struct itimerspec cur;
CHECK(timerfd_gettime(fd, &cur) == 0 && cur.it_value.tv_nsec + cur.it_value.tv_sec > 0, "gettime while armed");
uint64_t n = 0;
CHECK(read(fd, &n, sizeof n) == 8, "blocking read: errno %d", errno);
long long took = now_ms() - t0;
CHECK(n == 1, "one-shot count %llu", (unsigned long long)n);
CHECK(took >= 140 && took < 1000, "one-shot fired after %lld ms", took);
CHECK(timerfd_gettime(fd, &cur) == 0 && cur.it_value.tv_sec == 0 && cur.it_value.tv_nsec == 0, "disarmed after firing");
close(fd);
}
static void interval(void)
{
int fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK);
CHECK(fd >= 0, "timerfd_create(BOOTTIME): errno %d", errno);
CHECK(arm(fd, 60, 60) == 0, "settime interval");
struct itimerspec cur;
timerfd_gettime(fd, &cur);
CHECK(cur.it_interval.tv_sec == 0 && cur.it_interval.tv_nsec == 60000000, "gettime interval %ld", cur.it_interval.tv_nsec);
ms_sleep(700);
uint64_t n = 0;
CHECK(read(fd, &n, sizeof n) == 8, "read interval: errno %d", errno);
CHECK(n >= 5 && n <= 14, "interval count %llu after 700 ms of 60 ms periods", (unsigned long long)n);
ms_sleep(250);
uint64_t m = 0;
CHECK(read(fd, &m, sizeof m) == 8 && m >= 1 && m <= 7, "count after reset %llu", (unsigned long long)m);
struct itimerspec off = { 0 };
CHECK(timerfd_settime(fd, 0, &off, &cur) == 0 && cur.it_interval.tv_nsec == 60000000, "disarm returns old");
ms_sleep(150);
CHECK(read(fd, &m, sizeof m) == -1 && errno == EAGAIN, "disarmed: EAGAIN");
close(fd);
}
static void nonblocking_and_errors(void)
{
int fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
CHECK(fd >= 0, "timerfd_create(REALTIME)");
uint64_t n;
CHECK(read(fd, &n, sizeof n) == -1 && errno == EAGAIN, "disarmed read: EAGAIN (errno %d)", errno);
arm(fd, 5000, 0);
CHECK(read(fd, &n, sizeof n) == -1 && errno == EAGAIN, "not yet expired: EAGAIN");
char small[4];
CHECK(read(fd, small, sizeof small) == -1 && errno == EINVAL, "4-byte buffer: EINVAL (errno %d)", errno);
CHECK(write(fd, &n, sizeof n) == -1 && errno == EINVAL, "write: EINVAL");
struct timespec wall;
clock_gettime(CLOCK_REALTIME, &wall);
wall.tv_nsec += 120000000;
if (wall.tv_nsec >= 1000000000) {
wall.tv_sec++;
wall.tv_nsec -= 1000000000;
}
struct itimerspec abs = { { 0, 0 }, wall };
CHECK(timerfd_settime(fd, TFD_TIMER_ABSTIME, &abs, NULL) == 0, "ABSTIME settime");
struct pollfd p = { fd, POLLIN, 0 };
CHECK(poll(&p, 1, 2000) == 1 && (p.revents & POLLIN), "poll: ABSTIME timer fired");
CHECK(read(fd, &n, sizeof n) == 8 && n == 1, "ABSTIME count");
CHECK(poll(&p, 1, 0) == 0, "poll: not readable after read");
struct utsname u;
int rustbelt = uname(&u) == 0 && strncmp(u.version, "stage", 5) == 0;
int r = timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &abs, NULL);
CHECK(rustbelt ? r == -1 && errno == EINVAL : r == 0, "CANCEL_ON_SET: r %d errno %d", r, errno);
CHECK(timerfd_create(CLOCK_PROCESS_CPUTIME_ID, 0) == -1 && errno == EINVAL, "bad clock: EINVAL");
CHECK(timerfd_create(CLOCK_MONOTONIC, 1) == -1 && errno == EINVAL, "bad flags: EINVAL");
struct itimerspec bad = { { 0, 0 }, { 0, 1000000000 } };
CHECK(timerfd_settime(fd, 0, &bad, NULL) == -1 && errno == EINVAL, "tv_nsec too big: EINVAL");
struct itimerspec cur;
CHECK(timerfd_gettime(1, &cur) == -1 && errno == EINVAL, "not a timerfd: EINVAL");
CHECK(timerfd_gettime(999, &cur) == -1 && errno == EBADF, "bad fd: EBADF");
close(fd);
}
static void readiness(void)
{
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
int ep = epoll_create1(EPOLL_CLOEXEC);
struct epoll_event ev = { .events = EPOLLIN, .data.u64 = 0x1234 };
CHECK(epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev) == 0, "epoll_ctl ADD: errno %d", errno);
struct epoll_event out;
CHECK(epoll_wait(ep, &out, 1, 0) == 0, "epoll: nothing yet");
long long t0 = now_ms();
arm(fd, 100, 0);
int r = epoll_wait(ep, &out, 1, 3000);
long long took = now_ms() - t0;
CHECK(r == 1 && out.data.u64 == 0x1234 && (out.events & EPOLLIN), "epoll_wait woke: r %d", r);
CHECK(took >= 90 && took < 1500, "epoll woke after %lld ms", took);
uint64_t n = 0;
CHECK(read(fd, &n, sizeof n) == 8 && n == 1, "count after epoll");
CHECK(epoll_wait(ep, &out, 1, 0) == 0, "epoll: drained");
arm(fd, 80, 0);
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
struct timeval tv = { 2, 0 };
CHECK(select(fd + 1, &set, NULL, NULL, &tv) == 1 && FD_ISSET(fd, &set), "select woke");
read(fd, &n, sizeof n);
close(ep);
close(fd);
}
static void many(void)
{
int fds[40];
for (int i = 0; i < 40; i++) {
fds[i] = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
CHECK(fds[i] >= 0, "timerfd %d", i);
arm(fds[i], 10 + i, 20);
}
ms_sleep(120);
for (int i = 0; i < 30; i++)
close(fds[i]);
}
int main(void)
{
one_shot();
interval();
nonblocking_and_errors();
readiness();
many();
printf("timerfdtest: %d failed\n", fails);
return fails;
}
|