1*471dbed6Svisa /* $OpenBSD: kqueue-timer.c,v 1.5 2023/08/13 08:29:28 visa Exp $ */
2f79d7230Sblambert /*
3f79d7230Sblambert * Copyright (c) 2015 Bret Stephen Lambert <blambert@openbsd.org>
4f79d7230Sblambert *
5f79d7230Sblambert * Permission to use, copy, modify, and distribute this software for any
6f79d7230Sblambert * purpose with or without fee is hereby granted, provided that the above
7f79d7230Sblambert * copyright notice and this permission notice appear in all copies.
8f79d7230Sblambert *
9f79d7230Sblambert * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10f79d7230Sblambert * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11f79d7230Sblambert * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12f79d7230Sblambert * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13f79d7230Sblambert * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14f79d7230Sblambert * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15f79d7230Sblambert * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16f79d7230Sblambert */
17bd35765dSbluhm
18f79d7230Sblambert #include <sys/types.h>
19f79d7230Sblambert #include <sys/time.h>
20f79d7230Sblambert #include <sys/event.h>
21f79d7230Sblambert
22bd35765dSbluhm #include <err.h>
23c9604848Scheloha #include <errno.h>
24bd35765dSbluhm #include <stdio.h>
25*471dbed6Svisa #include <stdint.h>
26bd35765dSbluhm #include <string.h>
27595a2bfbSvisa #include <time.h>
28bd35765dSbluhm #include <unistd.h>
29bd35765dSbluhm
30bd35765dSbluhm #include "main.h"
31f79d7230Sblambert
32f79d7230Sblambert int
do_timer(void)33f79d7230Sblambert do_timer(void)
34f79d7230Sblambert {
35*471dbed6Svisa static const int units[] = {
36*471dbed6Svisa NOTE_SECONDS, NOTE_MSECONDS, NOTE_USECONDS, NOTE_NSECONDS
37*471dbed6Svisa };
38f79d7230Sblambert struct kevent ev;
39*471dbed6Svisa struct timespec ts, start, end, now;
40*471dbed6Svisa int64_t usecs;
41*471dbed6Svisa int i, kq, n;
42f79d7230Sblambert
43bd35765dSbluhm ASS((kq = kqueue()) >= 0,
44bd35765dSbluhm warn("kqueue"));
45f79d7230Sblambert
46f79d7230Sblambert memset(&ev, 0, sizeof(ev));
47f79d7230Sblambert ev.filter = EVFILT_TIMER;
48f79d7230Sblambert ev.flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
49f79d7230Sblambert ev.data = 500; /* 1/2 second in ms */
50f79d7230Sblambert
51bd35765dSbluhm n = kevent(kq, &ev, 1, NULL, 0, NULL);
52bd35765dSbluhm ASSX(n != -1);
53f79d7230Sblambert
54f79d7230Sblambert ts.tv_sec = 2; /* wait 2s for kqueue timeout */
55f79d7230Sblambert ts.tv_nsec = 0;
56f79d7230Sblambert
57bd35765dSbluhm n = kevent(kq, NULL, 0, &ev, 1, &ts);
58bd35765dSbluhm ASSX(n == 1);
59f79d7230Sblambert
60f79d7230Sblambert /* Now retry w/o EV_ONESHOT, as EV_CLEAR is implicit */
61f79d7230Sblambert
62f79d7230Sblambert memset(&ev, 0, sizeof(ev));
63f79d7230Sblambert ev.filter = EVFILT_TIMER;
64f79d7230Sblambert ev.flags = EV_ADD | EV_ENABLE;
65f79d7230Sblambert ev.data = 500; /* 1/2 second in ms */
66f79d7230Sblambert
67bd35765dSbluhm n = kevent(kq, &ev, 1, NULL, 0, NULL);
68bd35765dSbluhm ASSX(n != -1);
69f79d7230Sblambert
70f79d7230Sblambert ts.tv_sec = 2; /* wait 2s for kqueue timeout */
71f79d7230Sblambert ts.tv_nsec = 0;
72f79d7230Sblambert
73bd35765dSbluhm n = kevent(kq, NULL, 0, &ev, 1, &ts);
74bd35765dSbluhm ASSX(n == 1);
75f79d7230Sblambert
76*471dbed6Svisa /* Test with different time units */
77*471dbed6Svisa
78*471dbed6Svisa for (i = 0; i < sizeof(units) / sizeof(units[0]); i++) {
79*471dbed6Svisa memset(&ev, 0, sizeof(ev));
80*471dbed6Svisa ev.filter = EVFILT_TIMER;
81*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
82*471dbed6Svisa ev.fflags = units[i];
83*471dbed6Svisa ev.data = 1;
84*471dbed6Svisa
85*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
86*471dbed6Svisa ASSX(n != -1);
87*471dbed6Svisa
88*471dbed6Svisa ts.tv_sec = 2; /* wait 2s for kqueue timeout */
89*471dbed6Svisa ts.tv_nsec = 0;
90*471dbed6Svisa
91*471dbed6Svisa n = kevent(kq, NULL, 0, &ev, 1, &ts);
92*471dbed6Svisa ASSX(n == 1);
93*471dbed6Svisa
94*471dbed6Svisa /* Delete timer to clear EV_CLEAR */
95*471dbed6Svisa
96*471dbed6Svisa memset(&ev, 0, sizeof(ev));
97*471dbed6Svisa ev.filter = EVFILT_TIMER;
98*471dbed6Svisa ev.flags = EV_DELETE;
99*471dbed6Svisa
100*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
101*471dbed6Svisa ASSX(n != -1);
102*471dbed6Svisa
103*471dbed6Svisa /* Test with NOTE_ABSTIME, deadline in the future */
104*471dbed6Svisa
105*471dbed6Svisa clock_gettime(CLOCK_MONOTONIC, &start);
106*471dbed6Svisa
107*471dbed6Svisa clock_gettime(CLOCK_REALTIME, &now);
108*471dbed6Svisa memset(&ev, 0, sizeof(ev));
109*471dbed6Svisa ev.filter = EVFILT_TIMER;
110*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
111*471dbed6Svisa ev.fflags = NOTE_ABSTIME | units[i];
112*471dbed6Svisa
113*471dbed6Svisa switch (units[i]) {
114*471dbed6Svisa case NOTE_SECONDS:
115*471dbed6Svisa ev.data = now.tv_sec + 1;
116*471dbed6Svisa break;
117*471dbed6Svisa case NOTE_MSECONDS:
118*471dbed6Svisa ev.data = now.tv_sec * 1000 + now.tv_nsec / 1000000
119*471dbed6Svisa + 100;
120*471dbed6Svisa break;
121*471dbed6Svisa case NOTE_USECONDS:
122*471dbed6Svisa ev.data = now.tv_sec * 1000000 + now.tv_nsec / 1000
123*471dbed6Svisa + 100 * 1000;
124*471dbed6Svisa break;
125*471dbed6Svisa case NOTE_NSECONDS:
126*471dbed6Svisa ev.data = now.tv_sec * 1000000000 + now.tv_nsec
127*471dbed6Svisa + 100 * 1000000;
128*471dbed6Svisa break;
129*471dbed6Svisa }
130*471dbed6Svisa
131*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
132*471dbed6Svisa ASSX(n != -1);
133*471dbed6Svisa
134*471dbed6Svisa ts.tv_sec = 2; /* wait 2s for kqueue timeout */
135*471dbed6Svisa ts.tv_nsec = 0;
136*471dbed6Svisa
137*471dbed6Svisa n = kevent(kq, NULL, 0, &ev, 1, &ts);
138*471dbed6Svisa ASSX(n == 1);
139*471dbed6Svisa
140*471dbed6Svisa clock_gettime(CLOCK_MONOTONIC, &end);
141*471dbed6Svisa timespecsub(&end, &start, &ts);
142*471dbed6Svisa usecs = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
143*471dbed6Svisa ASSX(usecs > 0);
144*471dbed6Svisa ASSX(usecs < 1500000); /* allow wide margin */
145*471dbed6Svisa
146*471dbed6Svisa /* Test with NOTE_ABSTIME, deadline in the past. */
147*471dbed6Svisa
148*471dbed6Svisa clock_gettime(CLOCK_MONOTONIC, &start);
149*471dbed6Svisa
150*471dbed6Svisa memset(&ev, 0, sizeof(ev));
151*471dbed6Svisa ev.filter = EVFILT_TIMER;
152*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
153*471dbed6Svisa ev.fflags = NOTE_ABSTIME | units[i];
154*471dbed6Svisa
155*471dbed6Svisa clock_gettime(CLOCK_REALTIME, &now);
156*471dbed6Svisa switch (units[i]) {
157*471dbed6Svisa case NOTE_SECONDS:
158*471dbed6Svisa ev.data = now.tv_sec - 1;
159*471dbed6Svisa break;
160*471dbed6Svisa case NOTE_MSECONDS:
161*471dbed6Svisa ev.data = now.tv_sec * 1000 + now.tv_nsec / 1000000
162*471dbed6Svisa - 100;
163*471dbed6Svisa break;
164*471dbed6Svisa case NOTE_USECONDS:
165*471dbed6Svisa ev.data = now.tv_sec * 1000000 + now.tv_nsec / 1000
166*471dbed6Svisa - 100 * 1000;
167*471dbed6Svisa break;
168*471dbed6Svisa case NOTE_NSECONDS:
169*471dbed6Svisa ev.data = now.tv_sec * 1000000000 + now.tv_nsec
170*471dbed6Svisa - 100 * 1000000;
171*471dbed6Svisa break;
172*471dbed6Svisa }
173*471dbed6Svisa
174*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
175*471dbed6Svisa ASSX(n != -1);
176*471dbed6Svisa
177*471dbed6Svisa n = kevent(kq, NULL, 0, &ev, 1, &ts);
178*471dbed6Svisa ASSX(n == 1);
179*471dbed6Svisa
180*471dbed6Svisa clock_gettime(CLOCK_MONOTONIC, &end);
181*471dbed6Svisa timespecsub(&end, &start, &ts);
182*471dbed6Svisa usecs = ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
183*471dbed6Svisa ASSX(usecs > 0);
184*471dbed6Svisa ASSX(usecs < 100000); /* allow wide margin */
185*471dbed6Svisa
186*471dbed6Svisa /* Test that the event remains active */
187*471dbed6Svisa
188*471dbed6Svisa ts.tv_sec = 2; /* wait 2s for kqueue timeout */
189*471dbed6Svisa ts.tv_nsec = 0;
190*471dbed6Svisa
191*471dbed6Svisa n = kevent(kq, NULL, 0, &ev, 1, &ts);
192*471dbed6Svisa ASSX(n == 1);
193*471dbed6Svisa }
194*471dbed6Svisa
195f79d7230Sblambert return (0);
196f79d7230Sblambert }
197c9604848Scheloha
198c9604848Scheloha int
do_invalid_timer(void)199c9604848Scheloha do_invalid_timer(void)
200c9604848Scheloha {
201c9604848Scheloha int i, kq, n;
202c9604848Scheloha struct kevent ev;
203c9604848Scheloha struct timespec invalid_ts[3] = { {-1, 0}, {0, -1}, {0, 1000000000L} };
204c9604848Scheloha
205c9604848Scheloha ASS((kq = kqueue()) >= 0,
206c9604848Scheloha warn("kqueue"));
207c9604848Scheloha
208c9604848Scheloha memset(&ev, 0, sizeof(ev));
209c9604848Scheloha ev.filter = EVFILT_TIMER;
210c9604848Scheloha ev.flags = EV_ADD | EV_ENABLE;
211c9604848Scheloha ev.data = 500; /* 1/2 second in ms */
212c9604848Scheloha
213c9604848Scheloha n = kevent(kq, &ev, 1, NULL, 0, NULL);
214c9604848Scheloha ASSX(n != -1);
215c9604848Scheloha
216c9604848Scheloha for (i = 0; i < 3; i++) {
217c9604848Scheloha n = kevent(kq, NULL, 0, &ev, 1, &invalid_ts[i]);
218c9604848Scheloha ASS(n == -1 && errno == EINVAL,
219c9604848Scheloha warn("kevent: timeout %lld %ld",
220c9604848Scheloha (long long)invalid_ts[i].tv_sec, invalid_ts[i].tv_nsec));
221c9604848Scheloha }
222c9604848Scheloha
223*471dbed6Svisa /* Test invalid fflags */
224*471dbed6Svisa
225*471dbed6Svisa memset(&ev, 0, sizeof(ev));
226*471dbed6Svisa ev.filter = EVFILT_TIMER;
227*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
228*471dbed6Svisa ev.fflags = ~NOTE_SECONDS;
229*471dbed6Svisa ev.data = 1;
230*471dbed6Svisa
231*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
232*471dbed6Svisa ASSX(n == -1 && errno == EINVAL);
233*471dbed6Svisa
234*471dbed6Svisa memset(&ev, 0, sizeof(ev));
235*471dbed6Svisa ev.filter = EVFILT_TIMER;
236*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
237*471dbed6Svisa ev.fflags = NOTE_MSECONDS;
238*471dbed6Svisa ev.data = 500;
239*471dbed6Svisa
240*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
241*471dbed6Svisa ASSX(n == 0);
242*471dbed6Svisa
243*471dbed6Svisa /* Modify the existing timer */
244*471dbed6Svisa
245*471dbed6Svisa memset(&ev, 0, sizeof(ev));
246*471dbed6Svisa ev.filter = EVFILT_TIMER;
247*471dbed6Svisa ev.flags = EV_ADD | EV_ENABLE;
248*471dbed6Svisa ev.fflags = ~NOTE_SECONDS;
249*471dbed6Svisa ev.data = 1;
250*471dbed6Svisa
251*471dbed6Svisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
252*471dbed6Svisa ASSX(n == -1 && errno == EINVAL);
253*471dbed6Svisa
254c9604848Scheloha return (0);
255c9604848Scheloha }
256595a2bfbSvisa
257595a2bfbSvisa int
do_reset_timer(void)258595a2bfbSvisa do_reset_timer(void)
259595a2bfbSvisa {
260595a2bfbSvisa int kq, msecs, n;
261595a2bfbSvisa struct kevent ev;
262595a2bfbSvisa struct timespec ts, start, end;
263595a2bfbSvisa
264595a2bfbSvisa ASS((kq = kqueue()) >= 0,
265595a2bfbSvisa warn("kqueue"));
266595a2bfbSvisa
267595a2bfbSvisa clock_gettime(CLOCK_MONOTONIC, &start);
268595a2bfbSvisa
269595a2bfbSvisa memset(&ev, 0, sizeof(ev));
270595a2bfbSvisa ev.filter = EVFILT_TIMER;
271595a2bfbSvisa ev.flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
272595a2bfbSvisa ev.data = 10;
273595a2bfbSvisa
274595a2bfbSvisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
275595a2bfbSvisa ASSX(n != -1);
276595a2bfbSvisa
277595a2bfbSvisa /* Let the timer expire. */
278595a2bfbSvisa usleep(100000);
279595a2bfbSvisa
280595a2bfbSvisa /* Reset the expired timer. */
281595a2bfbSvisa ev.data = 60000;
282595a2bfbSvisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
283595a2bfbSvisa ASSX(n != -1);
284595a2bfbSvisa
285595a2bfbSvisa /* Check that no event is pending. */
286595a2bfbSvisa ts.tv_sec = 0;
287595a2bfbSvisa ts.tv_nsec = 0;
288595a2bfbSvisa n = kevent(kq, NULL, 0, &ev, 1, &ts);
289595a2bfbSvisa ASSX(n == 0);
290595a2bfbSvisa
291595a2bfbSvisa /* Reset again for quick expiry. */
292595a2bfbSvisa memset(&ev, 0, sizeof(ev));
293595a2bfbSvisa ev.filter = EVFILT_TIMER;
294595a2bfbSvisa ev.flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
295595a2bfbSvisa ev.data = 100;
296595a2bfbSvisa n = kevent(kq, &ev, 1, NULL, 0, NULL);
297595a2bfbSvisa ASSX(n != -1);
298595a2bfbSvisa
299595a2bfbSvisa /* Wait for expiry. */
300595a2bfbSvisa n = kevent(kq, NULL, 0, &ev, 1, NULL);
301595a2bfbSvisa ASSX(n == 1);
302595a2bfbSvisa
303595a2bfbSvisa clock_gettime(CLOCK_MONOTONIC, &end);
304595a2bfbSvisa timespecsub(&end, &start, &ts);
305595a2bfbSvisa msecs = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
306595a2bfbSvisa ASSX(msecs > 200);
307595a2bfbSvisa ASSX(msecs < 5000); /* allow wide margin */
308595a2bfbSvisa
309595a2bfbSvisa return (0);
310595a2bfbSvisa }
311