1 /*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * $FreeBSD: stable/12/tests/sys/kqueue/libkqueue/timer.c 370744 2021-10-06 14:54:34Z kevans $
17 */
18
19 #include "common.h"
20 #include <sys/time.h>
21
22 #define MILLION 1000000
23 #define THOUSAND 1000
24 #define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */
25 #define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */
26 #define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */
27 #define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */
28
29 #ifndef CLOCK_BOOTTIME
30 #define CLOCK_BOOTTIME CLOCK_UPTIME
31 #endif
32
33 /* Get the current time with microsecond precision. Used for
34 * sub-second timing to make some timer tests run faster.
35 */
36 static uint64_t
now(void)37 now(void)
38 {
39 struct timeval tv;
40
41 gettimeofday(&tv, NULL);
42 /* Promote potentially 32-bit time_t to uint64_t before conversion. */
43 return SEC_TO_US((uint64_t)tv.tv_sec) + tv.tv_usec;
44 }
45
46 /* Sleep for a given number of milliseconds. The timeout is assumed to
47 * be less than 1 second.
48 */
49 void
mssleep(int t)50 mssleep(int t)
51 {
52 struct timespec stime = {
53 .tv_sec = 0,
54 .tv_nsec = US_TO_NS(MS_TO_US(t)),
55 };
56
57 nanosleep(&stime, NULL);
58 }
59
60 /* Sleep for a given number of microseconds. The timeout is assumed to
61 * be less than 1 second.
62 */
63 void
ussleep(int t)64 ussleep(int t)
65 {
66 struct timespec stime = {
67 .tv_sec = 0,
68 .tv_nsec = US_TO_NS(t),
69 };
70
71 nanosleep(&stime, NULL);
72 }
73
74 void
test_kevent_timer_add(void)75 test_kevent_timer_add(void)
76 {
77 const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)";
78 struct kevent kev;
79
80 test_begin(test_id);
81
82 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
83 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
84 err(1, "%s", test_id);
85
86 success();
87 }
88
89 void
test_kevent_timer_del(void)90 test_kevent_timer_del(void)
91 {
92 const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)";
93 struct kevent kev;
94
95 test_begin(test_id);
96
97 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
98 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
99 err(1, "%s", test_id);
100
101 test_no_kevents();
102
103 success();
104 }
105
106 void
test_kevent_timer_get(void)107 test_kevent_timer_get(void)
108 {
109 const char *test_id = "kevent(EVFILT_TIMER, wait)";
110 struct kevent kev;
111
112 test_begin(test_id);
113
114 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
115 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
116 err(1, "%s", test_id);
117
118 kev.flags |= EV_CLEAR;
119 kev.data = 1;
120 kevent_cmp(&kev, kevent_get(kqfd));
121
122 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
123 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
124 err(1, "%s", test_id);
125
126 success();
127 }
128
129 static void
test_oneshot(void)130 test_oneshot(void)
131 {
132 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)";
133 struct kevent kev;
134
135 test_begin(test_id);
136
137 test_no_kevents();
138
139 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL);
140 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
141 err(1, "%s", test_id);
142
143 /* Retrieve the event */
144 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
145 kev.data = 1;
146 kevent_cmp(&kev, kevent_get(kqfd));
147
148 /* Check if the event occurs again */
149 sleep(3);
150 test_no_kevents();
151
152
153 success();
154 }
155
156 static void
test_periodic(void)157 test_periodic(void)
158 {
159 const char *test_id = "kevent(EVFILT_TIMER, periodic)";
160 struct kevent kev;
161
162 test_begin(test_id);
163
164 test_no_kevents();
165
166 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL);
167 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
168 err(1, "%s", test_id);
169
170 /* Retrieve the event */
171 kev.flags = EV_ADD | EV_CLEAR;
172 kev.data = 1;
173 kevent_cmp(&kev, kevent_get(kqfd));
174
175 /* Check if the event occurs again */
176 sleep(1);
177 kevent_cmp(&kev, kevent_get(kqfd));
178
179 /* Delete the event */
180 kev.flags = EV_DELETE;
181 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
182 err(1, "%s", test_id);
183
184 success();
185 }
186
187 static void
disable_and_enable(void)188 disable_and_enable(void)
189 {
190 const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)";
191 struct kevent kev;
192
193 test_begin(test_id);
194
195 test_no_kevents();
196
197 /* Add the watch and immediately disable it */
198 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL);
199 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
200 err(1, "%s", test_id);
201 kev.flags = EV_DISABLE;
202 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
203 err(1, "%s", test_id);
204 test_no_kevents();
205
206 /* Re-enable and check again */
207 kev.flags = EV_ENABLE;
208 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
209 err(1, "%s", test_id);
210
211 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
212 kev.data = 1;
213 kevent_cmp(&kev, kevent_get(kqfd));
214
215 success();
216 }
217
218 static void
test_abstime(void)219 test_abstime(void)
220 {
221 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)";
222 struct kevent kev;
223 uint64_t end, start, stop;
224 const int timeout_sec = 3;
225
226 test_begin(test_id);
227
228 test_no_kevents();
229
230 start = now();
231 end = start + SEC_TO_US(timeout_sec);
232 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
233 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
234 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
235 err(1, "%s", test_id);
236
237 /* Retrieve the event */
238 kev.flags = EV_ADD | EV_ONESHOT;
239 kev.data = 1;
240 kev.fflags = 0;
241 kevent_cmp(&kev, kevent_get(kqfd));
242
243 stop = now();
244 if (stop < end)
245 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
246 /* Check if the event occurs again */
247 sleep(3);
248 test_no_kevents();
249
250 success();
251 }
252
253 static void
test_abstime_epoch(void)254 test_abstime_epoch(void)
255 {
256 const char *test_id = "kevent(EVFILT_TIMER (EPOCH), NOTE_ABSTIME)";
257 struct kevent kev;
258
259 test_begin(test_id);
260
261 test_no_kevents();
262
263 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, NOTE_ABSTIME, 0,
264 NULL);
265 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
266 err(1, "%s", test_id);
267
268 /* Retrieve the event */
269 kev.flags = EV_ADD;
270 kev.data = 1;
271 kev.fflags = 0;
272 kevent_cmp(&kev, kevent_get(kqfd));
273
274 /* Delete the event */
275 kev.flags = EV_DELETE;
276 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
277 err(1, "%s", test_id);
278
279 success();
280 }
281
282 static void
test_abstime_preboot(void)283 test_abstime_preboot(void)
284 {
285 const char *test_id = "kevent(EVFILT_TIMER (PREBOOT), EV_ONESHOT, NOTE_ABSTIME)";
286 struct kevent kev;
287 struct timespec btp;
288 uint64_t end, start, stop;
289
290 test_begin(test_id);
291
292 test_no_kevents();
293
294 /*
295 * We'll expire it at just before system boot (roughly) with the hope that
296 * we'll get an ~immediate expiration, just as we do for any value specified
297 * between system boot and now.
298 */
299 start = now();
300 if (clock_gettime(CLOCK_BOOTTIME, &btp) != 0)
301 err(1, "%s", test_id);
302
303 end = start - SEC_TO_US(btp.tv_sec + 1);
304 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
305 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
306 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
307 err(1, "%s", test_id);
308
309 /* Retrieve the event */
310 kev.flags = EV_ADD | EV_ONESHOT;
311 kev.data = 1;
312 kev.fflags = 0;
313 kevent_cmp(&kev, kevent_get(kqfd));
314
315 stop = now();
316 if (stop < end)
317 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
318 /* Check if the event occurs again */
319 sleep(3);
320 test_no_kevents();
321
322 success();
323 }
324
325 static void
test_abstime_postboot(void)326 test_abstime_postboot(void)
327 {
328 const char *test_id = "kevent(EVFILT_TIMER (POSTBOOT), EV_ONESHOT, NOTE_ABSTIME)";
329 struct kevent kev;
330 uint64_t end, start, stop;
331 const int timeout_sec = 1;
332
333 test_begin(test_id);
334
335 test_no_kevents();
336
337 /*
338 * Set a timer for 1 second ago, it should fire immediately rather than
339 * being rejected.
340 */
341 start = now();
342 end = start - SEC_TO_US(timeout_sec);
343 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
344 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
345 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
346 err(1, "%s", test_id);
347
348 /* Retrieve the event */
349 kev.flags = EV_ADD | EV_ONESHOT;
350 kev.data = 1;
351 kev.fflags = 0;
352 kevent_cmp(&kev, kevent_get(kqfd));
353
354 stop = now();
355 if (stop < end)
356 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
357 /* Check if the event occurs again */
358 sleep(3);
359 test_no_kevents();
360
361 success();
362 }
363
364 static void
test_update(void)365 test_update(void)
366 {
367 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
368 struct kevent kev;
369 long elapsed;
370 uint64_t start;
371
372 test_begin(test_id);
373
374 test_no_kevents();
375
376 /* First set the timer to 1 second */
377 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
378 NOTE_USECONDS, SEC_TO_US(1), (void *)1);
379 start = now();
380 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
381 err(1, "%s", test_id);
382
383 /* Now reduce the timer to 1 ms */
384 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
385 NOTE_USECONDS, MS_TO_US(1), (void *)2);
386 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
387 err(1, "%s", test_id);
388
389 /* Wait for the event */
390 kev.flags |= EV_CLEAR;
391 kev.fflags &= ~NOTE_USECONDS;
392 kev.data = 1;
393 kevent_cmp(&kev, kevent_get(kqfd));
394 elapsed = now() - start;
395
396 /* Check that the timer expired after at least 1 ms, but less than
397 * 1 second. This check is to make sure that the original 1 second
398 * timeout was not used.
399 */
400 printf("timer expired after %ld us\n", elapsed);
401 if (elapsed < MS_TO_US(1))
402 errx(1, "early timer expiration: %ld us", elapsed);
403 if (elapsed > SEC_TO_US(1))
404 errx(1, "late timer expiration: %ld us", elapsed);
405
406 success();
407 }
408
409 static void
test_update_equal(void)410 test_update_equal(void)
411 {
412 const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
413 struct kevent kev;
414 long elapsed;
415 uint64_t start;
416
417 test_begin(test_id);
418
419 test_no_kevents();
420
421 /* First set the timer to 1 ms */
422 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
423 NOTE_USECONDS, MS_TO_US(1), NULL);
424 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
425 err(1, "%s", test_id);
426
427 /* Sleep for a significant fraction of the timeout. */
428 ussleep(600);
429
430 /* Now re-add the timer with the same parameters */
431 start = now();
432 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
433 err(1, "%s", test_id);
434
435 /* Wait for the event */
436 kev.flags |= EV_CLEAR;
437 kev.fflags &= ~NOTE_USECONDS;
438 kev.data = 1;
439 kevent_cmp(&kev, kevent_get(kqfd));
440 elapsed = now() - start;
441
442 /* Check that the timer expired after at least 1 ms. This check is
443 * to make sure that the timer re-started and that the event is
444 * not from the original add of the timer.
445 */
446 printf("timer expired after %ld us\n", elapsed);
447 if (elapsed < MS_TO_US(1))
448 errx(1, "early timer expiration: %ld us", elapsed);
449
450 success();
451 }
452
453 static void
test_update_expired(void)454 test_update_expired(void)
455 {
456 const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
457 struct kevent kev;
458 long elapsed;
459 uint64_t start;
460
461 test_begin(test_id);
462
463 test_no_kevents();
464
465 /* Set the timer to 1ms */
466 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
467 NOTE_USECONDS, MS_TO_US(1), NULL);
468 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
469 err(1, "%s", test_id);
470
471 /* Wait for 2 ms to give the timer plenty of time to expire. */
472 mssleep(2);
473
474 /* Now re-add the timer */
475 start = now();
476 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
477 err(1, "%s", test_id);
478
479 /* Wait for the event */
480 kev.flags |= EV_CLEAR;
481 kev.fflags &= ~NOTE_USECONDS;
482 kev.data = 1;
483 kevent_cmp(&kev, kevent_get(kqfd));
484 elapsed = now() - start;
485
486 /* Check that the timer expired after at least 1 ms. This check
487 * is to make sure that the timer re-started and that the event is
488 * not from the original add (and expiration) of the timer.
489 */
490 printf("timer expired after %ld us\n", elapsed);
491 if (elapsed < MS_TO_US(1))
492 errx(1, "early timer expiration: %ld us", elapsed);
493
494 /* Make sure the re-added timer does not fire. In other words,
495 * test that the event received above was the only event from the
496 * add and re-add of the timer.
497 */
498 mssleep(2);
499 test_no_kevents();
500
501 success();
502 }
503
504 static void
test_update_periodic(void)505 test_update_periodic(void)
506 {
507 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
508 struct kevent kev;
509 long elapsed;
510 uint64_t start, stop;
511
512 test_begin(test_id);
513
514 test_no_kevents();
515
516 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
517 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
518 err(1, "%s", test_id);
519
520 /* Retrieve the event */
521 kev.flags = EV_ADD | EV_CLEAR;
522 kev.data = 1;
523 kevent_cmp(&kev, kevent_get(kqfd));
524
525 /* Check if the event occurs again */
526 sleep(1);
527 kevent_cmp(&kev, kevent_get(kqfd));
528
529 /* Re-add with new timeout. */
530 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
531 start = now();
532 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
533 err(1, "%s", test_id);
534
535 /* Retrieve the event */
536 kev.flags = EV_ADD | EV_CLEAR;
537 kev.data = 1;
538 kevent_cmp(&kev, kevent_get(kqfd));
539
540 stop = now();
541 elapsed = stop - start;
542
543 /* Check that the timer expired after at least 2 ms.
544 */
545 printf("timer expired after %ld us\n", elapsed);
546 if (elapsed < MS_TO_US(2))
547 errx(1, "early timer expiration: %ld us", elapsed);
548
549 /* Delete the event */
550 kev.flags = EV_DELETE;
551 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
552 err(1, "%s", test_id);
553
554 success();
555 }
556
557 static void
test_update_timing(void)558 test_update_timing(void)
559 {
560 #define MIN_SLEEP 500
561 #define MAX_SLEEP 1500
562 const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
563 struct kevent kev;
564 int iteration;
565 int sleeptime;
566 long elapsed;
567 uint64_t start, stop;
568
569 test_begin(test_id);
570
571 test_no_kevents();
572
573 /* Re-try the update tests with a variety of delays between the
574 * original timer activation and the update of the timer. The goal
575 * is to show that in all cases the only timer event that is
576 * received is from the update and not the original timer add.
577 */
578 for (sleeptime = MIN_SLEEP, iteration = 1;
579 sleeptime < MAX_SLEEP;
580 ++sleeptime, ++iteration) {
581
582 /* First set the timer to 1 ms */
583 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
584 NOTE_USECONDS, MS_TO_US(1), NULL);
585 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
586 err(1, "%s", test_id);
587
588 /* Delay; the delay ranges from less than to greater than the
589 * timer period.
590 */
591 ussleep(sleeptime);
592
593 /* Now re-add the timer with the same parameters */
594 start = now();
595 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
596 err(1, "%s", test_id);
597
598 /* Wait for the event */
599 kev.flags |= EV_CLEAR;
600 kev.fflags &= ~NOTE_USECONDS;
601 kev.data = 1;
602 kevent_cmp(&kev, kevent_get(kqfd));
603 stop = now();
604 elapsed = stop - start;
605
606 /* Check that the timer expired after at least 1 ms. This
607 * check is to make sure that the timer re-started and that
608 * the event is not from the original add of the timer.
609 */
610 if (elapsed < MS_TO_US(1))
611 errx(1, "early timer expiration: %ld us", elapsed);
612
613 /* Make sure the re-added timer does not fire. In other words,
614 * test that the event received above was the only event from
615 * the add and re-add of the timer.
616 */
617 mssleep(2);
618 test_no_kevents_quietly();
619 }
620
621 success();
622 }
623
624 void
test_evfilt_timer()625 test_evfilt_timer()
626 {
627 kqfd = kqueue();
628 test_kevent_timer_add();
629 test_kevent_timer_del();
630 test_kevent_timer_get();
631 test_oneshot();
632 test_periodic();
633 test_abstime();
634 test_abstime_epoch();
635 test_abstime_preboot();
636 test_abstime_postboot();
637 test_update();
638 test_update_equal();
639 test_update_expired();
640 test_update_timing();
641 test_update_periodic();
642 disable_and_enable();
643 close(kqfd);
644 }
645