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