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