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