timer.c (bca9d05fdb058aa709621661c2feccae8940d94b) timer.c (95c05062ec15cf323488d4c5e1986f5866bf7464)
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

--- 5 unchanged lines hidden (view full) ---

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
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

--- 5 unchanged lines hidden (view full) ---

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
22int kqfd;
23
29int kqfd;
30
31/* Get the current time with microsecond precision. Used for
32 * sub-second timing to make some timer tests run faster.
33 */
34static long
35now(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 */
24void
47void
48mssleep(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 */
62void
63ussleep(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
74void
25test_kevent_timer_add(void)
26{
27 const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)";
28 struct kevent kev;
29
30 test_begin(test_id);
31
32 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);

--- 151 unchanged lines hidden (view full) ---

184 err(1, "%s", test_id);
185
186 /* Retrieve the event */
187 kev.flags = EV_ADD | EV_ONESHOT;
188 kev.data = 1;
189 kev.fflags = 0;
190 kevent_cmp(&kev, kevent_get(kqfd));
191 if (time(NULL) < when + timeout)
75test_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);

--- 151 unchanged lines hidden (view full) ---

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)
192 err(1, "too early %jd %jd", time(), when + timeout);
242 err(1, "too early %jd %jd", time(NULL), when + timeout);
193
194 /* Check if the event occurs again */
195 sleep(3);
196 test_no_kevents();
197
198 success();
199}
200
243
244 /* Check if the event occurs again */
245 sleep(3);
246 test_no_kevents();
247
248 success();
249}
250
251static void
252test_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
296static void
297test_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
340static void
341test_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
391static void
392test_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
445static void
446test_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
201void
202test_evfilt_timer()
203{
204 kqfd = kqueue();
513void
514test_evfilt_timer()
515{
516 kqfd = kqueue();
205 test_kevent_timer_add();
206 test_kevent_timer_del();
207 test_kevent_timer_get();
208 test_oneshot();
209 test_periodic();
210 test_abstime();
211 disable_and_enable();
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();
212 close(kqfd);
213}
529 close(kqfd);
530}