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