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