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