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