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