xref: /freebsd/tests/sys/kqueue/libkqueue/timer.c (revision c6806455665e3c46e93083f431c036f0e8c0620c)
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 int kqfd;
30 
31 /* Get the current time with microsecond precision. Used for
32  * sub-second timing to make some timer tests run faster.
33  */
34 static long
35 now(void)
36 {
37 
38 	struct timeval tv;
39 
40 	gettimeofday(&tv, NULL);
41 	return SEC_TO_US(tv.tv_sec) + tv.tv_usec;
42 }
43 
44 /* Sleep for a given number of milliseconds. The timeout is assumed to
45  * be less than 1 second.
46  */
47 void
48 mssleep(int t)
49 {
50 
51 	struct timespec stime = {
52 	    .tv_sec = 0,
53 	    .tv_nsec = US_TO_NS(MS_TO_US(t)),
54 	};
55 
56 	nanosleep(&stime, NULL);
57 }
58 
59 /* Sleep for a given number of microseconds. The timeout is assumed to
60  * be less than 1 second.
61  */
62 void
63 ussleep(int t)
64 {
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
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
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
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
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
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
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
219 test_abstime(void)
220 {
221     const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)";
222     struct kevent kev;
223     time_t when;
224     const int timeout = 3;
225 
226     test_begin(test_id);
227 
228     test_no_kevents();
229 
230     when = time(NULL);
231     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
232       NOTE_ABSTIME | NOTE_SECONDS, when + timeout, NULL);
233     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
234         err(1, "%s", test_id);
235 
236     /* Retrieve the event */
237     kev.flags = EV_ADD | EV_ONESHOT;
238     kev.data = 1;
239     kev.fflags = 0;
240     kevent_cmp(&kev, kevent_get(kqfd));
241     if (time(NULL) < when + timeout)
242 	err(1, "too early %jd %jd", time(NULL), when + timeout);
243 
244     /* Check if the event occurs again */
245     sleep(3);
246     test_no_kevents();
247 
248     success();
249 }
250 
251 static void
252 test_update(void)
253 {
254     const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
255     struct kevent kev;
256     long elapsed;
257     long start;
258 
259     test_begin(test_id);
260 
261     test_no_kevents();
262 
263     /* First set the timer to 1 second */
264     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
265 	NOTE_USECONDS, SEC_TO_US(1), (void *)1);
266     start = now();
267     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
268 	err(1, "%s", test_id);
269 
270     /* Now reduce the timer to 1 ms */
271     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
272 	NOTE_USECONDS, MS_TO_US(1), (void *)2);
273     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
274 	err(1, "%s", test_id);
275 
276     /* Wait for the event */
277     kev.flags |= EV_CLEAR;
278     kev.fflags &= ~NOTE_USECONDS;
279     kev.data = 1;
280     kevent_cmp(&kev, kevent_get(kqfd));
281     elapsed = now() - start;
282 
283     /* Check that the timer expired after at least 1 ms, but less than
284      * 1 second. This check is to make sure that the original 1 second
285      * timeout was not used.
286      */
287     printf("timer expired after %ld us\n", elapsed);
288     if (elapsed < MS_TO_US(1))
289 	errx(1, "early timer expiration: %ld us", elapsed);
290     if (elapsed > SEC_TO_US(1))
291 	errx(1, "late timer expiration: %ld us", elapsed);
292 
293     success();
294 }
295 
296 static void
297 test_update_equal(void)
298 {
299     const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
300     struct kevent kev;
301     long elapsed;
302     long start;
303 
304     test_begin(test_id);
305 
306     test_no_kevents();
307 
308     /* First set the timer to 1 ms */
309     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
310 	NOTE_USECONDS, MS_TO_US(1), NULL);
311     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
312 	err(1, "%s", test_id);
313 
314     /* Sleep for a significant fraction of the timeout. */
315     ussleep(600);
316 
317     /* Now re-add the timer with the same parameters */
318     start = now();
319     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
320 	err(1, "%s", test_id);
321 
322     /* Wait for the event */
323     kev.flags |= EV_CLEAR;
324     kev.fflags &= ~NOTE_USECONDS;
325     kev.data = 1;
326     kevent_cmp(&kev, kevent_get(kqfd));
327     elapsed = now() - start;
328 
329     /* Check that the timer expired after at least 1 ms. This check is
330      * to make sure that the timer re-started and that the event is
331      * not from the original add of the timer.
332      */
333     printf("timer expired after %ld us\n", elapsed);
334     if (elapsed < MS_TO_US(1))
335 	errx(1, "early timer expiration: %ld us", elapsed);
336 
337     success();
338 }
339 
340 static void
341 test_update_expired(void)
342 {
343     const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
344     struct kevent kev;
345     long elapsed;
346     long start;
347 
348     test_begin(test_id);
349 
350     test_no_kevents();
351 
352     /* Set the timer to 1ms */
353     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
354 	NOTE_USECONDS, MS_TO_US(1), NULL);
355     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
356 	err(1, "%s", test_id);
357 
358     /* Wait for 2 ms to give the timer plenty of time to expire. */
359     mssleep(2);
360 
361     /* Now re-add the timer */
362     start = now();
363     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
364 	err(1, "%s", test_id);
365 
366     /* Wait for the event */
367     kev.flags |= EV_CLEAR;
368     kev.fflags &= ~NOTE_USECONDS;
369     kev.data = 1;
370     kevent_cmp(&kev, kevent_get(kqfd));
371     elapsed = now() - start;
372 
373     /* Check that the timer expired after at least 1 ms.  This check
374      * is to make sure that the timer re-started and that the event is
375      * not from the original add (and expiration) of the timer.
376      */
377     printf("timer expired after %ld us\n", elapsed);
378     if (elapsed < MS_TO_US(1))
379 	errx(1, "early timer expiration: %ld us", elapsed);
380 
381     /* Make sure the re-added timer does not fire. In other words,
382      * test that the event received above was the only event from the
383      * add and re-add of the timer.
384      */
385     mssleep(2);
386     test_no_kevents();
387 
388     success();
389 }
390 
391 static void
392 test_update_periodic(void)
393 {
394     const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
395     struct kevent kev;
396     long elapsed;
397     long start;
398     long stop;
399 
400     test_begin(test_id);
401 
402     test_no_kevents();
403 
404     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
405     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
406 	err(1, "%s", test_id);
407 
408     /* Retrieve the event */
409     kev.flags = EV_ADD | EV_CLEAR;
410     kev.data = 1;
411     kevent_cmp(&kev, kevent_get(kqfd));
412 
413     /* Check if the event occurs again */
414     sleep(1);
415     kevent_cmp(&kev, kevent_get(kqfd));
416 
417     /* Re-add with new timeout. */
418     EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
419     start = now();
420     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
421 	err(1, "%s", test_id);
422 
423     /* Retrieve the event */
424     kev.flags = EV_ADD | EV_CLEAR;
425     kev.data = 1;
426     kevent_cmp(&kev, kevent_get(kqfd));
427 
428     stop = now();
429     elapsed = stop - start;
430 
431     /* Check that the timer expired after at least 2 ms.
432      */
433     printf("timer expired after %ld us\n", elapsed);
434     if (elapsed < MS_TO_US(2))
435 	errx(1, "early timer expiration: %ld us", elapsed);
436 
437     /* Delete the event */
438     kev.flags = EV_DELETE;
439     if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
440 	err(1, "%s", test_id);
441 
442     success();
443 }
444 
445 static void
446 test_update_timing(void)
447 {
448 #define	MIN_SLEEP 500
449 #define	MAX_SLEEP 1500
450     const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
451     struct kevent kev;
452     int iteration;
453     int sleeptime;
454     long elapsed;
455     long start;
456     long stop;
457 
458     test_begin(test_id);
459 
460     test_no_kevents();
461 
462     /* Re-try the update tests with a variety of delays between the
463      * original timer activation and the update of the timer. The goal
464      * is to show that in all cases the only timer event that is
465      * received is from the update and not the original timer add.
466      */
467     for (sleeptime = MIN_SLEEP, iteration = 1;
468 	 sleeptime < MAX_SLEEP;
469 	 ++sleeptime, ++iteration) {
470 
471 	/* First set the timer to 1 ms */
472 	EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
473 	    NOTE_USECONDS, MS_TO_US(1), NULL);
474 	if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
475 	    err(1, "%s", test_id);
476 
477 	/* Delay; the delay ranges from less than to greater than the
478 	 * timer period.
479 	 */
480 	ussleep(sleeptime);
481 
482 	/* Now re-add the timer with the same parameters */
483 	start = now();
484 	if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
485 	    err(1, "%s", test_id);
486 
487 	/* Wait for the event */
488 	kev.flags |= EV_CLEAR;
489 	kev.fflags &= ~NOTE_USECONDS;
490 	kev.data = 1;
491 	kevent_cmp(&kev, kevent_get(kqfd));
492 	stop = now();
493 	elapsed = stop - start;
494 
495 	/* Check that the timer expired after at least 1 ms. This
496 	 * check is to make sure that the timer re-started and that
497 	 * the event is not from the original add of the timer.
498 	 */
499 	if (elapsed < MS_TO_US(1))
500 	    errx(1, "early timer expiration: %ld us", elapsed);
501 
502 	/* Make sure the re-added timer does not fire. In other words,
503 	 * test that the event received above was the only event from
504 	 * the add and re-add of the timer.
505 	 */
506 	mssleep(2);
507 	test_no_kevents_quietly();
508     }
509 
510     success();
511 }
512 
513 void
514 test_evfilt_timer()
515 {
516 	kqfd = kqueue();
517 	test_kevent_timer_add();
518 	test_kevent_timer_del();
519 	test_kevent_timer_get();
520 	test_oneshot();
521 	test_periodic();
522 	test_abstime();
523 	test_update();
524 	test_update_equal();
525 	test_update_expired();
526 	test_update_timing();
527 	test_update_periodic();
528 	disable_and_enable();
529 	close(kqfd);
530 }
531