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 30 /* Get the current time with microsecond precision. Used for 31 * sub-second timing to make some timer tests run faster. 32 */ 33 static long 34 now(void) 35 { 36 struct timeval tv; 37 38 gettimeofday(&tv, NULL); 39 return SEC_TO_US(tv.tv_sec) + tv.tv_usec; 40 } 41 42 /* Sleep for a given number of milliseconds. The timeout is assumed to 43 * be less than 1 second. 44 */ 45 void 46 mssleep(int t) 47 { 48 struct timespec stime = { 49 .tv_sec = 0, 50 .tv_nsec = US_TO_NS(MS_TO_US(t)), 51 }; 52 53 nanosleep(&stime, NULL); 54 } 55 56 /* Sleep for a given number of microseconds. The timeout is assumed to 57 * be less than 1 second. 58 */ 59 void 60 ussleep(int t) 61 { 62 struct timespec stime = { 63 .tv_sec = 0, 64 .tv_nsec = US_TO_NS(t), 65 }; 66 67 nanosleep(&stime, NULL); 68 } 69 70 void 71 test_kevent_timer_add(void) 72 { 73 const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)"; 74 struct kevent kev; 75 76 test_begin(test_id); 77 78 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); 79 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 80 err(1, "%s", test_id); 81 82 success(); 83 } 84 85 void 86 test_kevent_timer_del(void) 87 { 88 const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)"; 89 struct kevent kev; 90 91 test_begin(test_id); 92 93 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); 94 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 95 err(1, "%s", test_id); 96 97 test_no_kevents(); 98 99 success(); 100 } 101 102 void 103 test_kevent_timer_get(void) 104 { 105 const char *test_id = "kevent(EVFILT_TIMER, wait)"; 106 struct kevent kev; 107 108 test_begin(test_id); 109 110 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); 111 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 112 err(1, "%s", test_id); 113 114 kev.flags |= EV_CLEAR; 115 kev.data = 1; 116 kevent_cmp(&kev, kevent_get(kqfd)); 117 118 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); 119 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 120 err(1, "%s", test_id); 121 122 success(); 123 } 124 125 static void 126 test_oneshot(void) 127 { 128 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)"; 129 struct kevent kev; 130 131 test_begin(test_id); 132 133 test_no_kevents(); 134 135 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL); 136 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 137 err(1, "%s", test_id); 138 139 /* Retrieve the event */ 140 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; 141 kev.data = 1; 142 kevent_cmp(&kev, kevent_get(kqfd)); 143 144 /* Check if the event occurs again */ 145 sleep(3); 146 test_no_kevents(); 147 148 149 success(); 150 } 151 152 static void 153 test_periodic(void) 154 { 155 const char *test_id = "kevent(EVFILT_TIMER, periodic)"; 156 struct kevent kev; 157 158 test_begin(test_id); 159 160 test_no_kevents(); 161 162 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL); 163 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 164 err(1, "%s", test_id); 165 166 /* Retrieve the event */ 167 kev.flags = EV_ADD | EV_CLEAR; 168 kev.data = 1; 169 kevent_cmp(&kev, kevent_get(kqfd)); 170 171 /* Check if the event occurs again */ 172 sleep(1); 173 kevent_cmp(&kev, kevent_get(kqfd)); 174 175 /* Delete the event */ 176 kev.flags = EV_DELETE; 177 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 178 err(1, "%s", test_id); 179 180 success(); 181 } 182 183 static void 184 disable_and_enable(void) 185 { 186 const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)"; 187 struct kevent kev; 188 189 test_begin(test_id); 190 191 test_no_kevents(); 192 193 /* Add the watch and immediately disable it */ 194 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL); 195 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 196 err(1, "%s", test_id); 197 kev.flags = EV_DISABLE; 198 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 199 err(1, "%s", test_id); 200 test_no_kevents(); 201 202 /* Re-enable and check again */ 203 kev.flags = EV_ENABLE; 204 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 205 err(1, "%s", test_id); 206 207 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; 208 kev.data = 1; 209 kevent_cmp(&kev, kevent_get(kqfd)); 210 211 success(); 212 } 213 214 static void 215 test_abstime(void) 216 { 217 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)"; 218 struct kevent kev; 219 time_t start; 220 time_t stop; 221 const int timeout = 3; 222 223 test_begin(test_id); 224 225 test_no_kevents(); 226 227 start = time(NULL); 228 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 229 NOTE_ABSTIME | NOTE_SECONDS, start + timeout, NULL); 230 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 231 err(1, "%s", test_id); 232 233 /* Retrieve the event */ 234 kev.flags = EV_ADD | EV_ONESHOT; 235 kev.data = 1; 236 kev.fflags = 0; 237 kevent_cmp(&kev, kevent_get(kqfd)); 238 stop = time(NULL); 239 if (stop < start + timeout) 240 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)(start + timeout)); 241 242 /* Check if the event occurs again */ 243 sleep(3); 244 test_no_kevents(); 245 246 success(); 247 } 248 249 static void 250 test_update(void) 251 { 252 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)"; 253 struct kevent kev; 254 long elapsed; 255 long start; 256 257 test_begin(test_id); 258 259 test_no_kevents(); 260 261 /* First set the timer to 1 second */ 262 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 263 NOTE_USECONDS, SEC_TO_US(1), (void *)1); 264 start = now(); 265 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 266 err(1, "%s", test_id); 267 268 /* Now reduce the timer to 1 ms */ 269 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 270 NOTE_USECONDS, MS_TO_US(1), (void *)2); 271 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 272 err(1, "%s", test_id); 273 274 /* Wait for the event */ 275 kev.flags |= EV_CLEAR; 276 kev.fflags &= ~NOTE_USECONDS; 277 kev.data = 1; 278 kevent_cmp(&kev, kevent_get(kqfd)); 279 elapsed = now() - start; 280 281 /* Check that the timer expired after at least 1 ms, but less than 282 * 1 second. This check is to make sure that the original 1 second 283 * timeout was not used. 284 */ 285 printf("timer expired after %ld us\n", elapsed); 286 if (elapsed < MS_TO_US(1)) 287 errx(1, "early timer expiration: %ld us", elapsed); 288 if (elapsed > SEC_TO_US(1)) 289 errx(1, "late timer expiration: %ld us", elapsed); 290 291 success(); 292 } 293 294 static void 295 test_update_equal(void) 296 { 297 const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)"; 298 struct kevent kev; 299 long elapsed; 300 long start; 301 302 test_begin(test_id); 303 304 test_no_kevents(); 305 306 /* First set the timer to 1 ms */ 307 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 308 NOTE_USECONDS, MS_TO_US(1), NULL); 309 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 310 err(1, "%s", test_id); 311 312 /* Sleep for a significant fraction of the timeout. */ 313 ussleep(600); 314 315 /* Now re-add the timer with the same parameters */ 316 start = now(); 317 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 318 err(1, "%s", test_id); 319 320 /* Wait for the event */ 321 kev.flags |= EV_CLEAR; 322 kev.fflags &= ~NOTE_USECONDS; 323 kev.data = 1; 324 kevent_cmp(&kev, kevent_get(kqfd)); 325 elapsed = now() - start; 326 327 /* Check that the timer expired after at least 1 ms. This check is 328 * to make sure that the timer re-started and that the event is 329 * not from the original add of the timer. 330 */ 331 printf("timer expired after %ld us\n", elapsed); 332 if (elapsed < MS_TO_US(1)) 333 errx(1, "early timer expiration: %ld us", elapsed); 334 335 success(); 336 } 337 338 static void 339 test_update_expired(void) 340 { 341 const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)"; 342 struct kevent kev; 343 long elapsed; 344 long start; 345 346 test_begin(test_id); 347 348 test_no_kevents(); 349 350 /* Set the timer to 1ms */ 351 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 352 NOTE_USECONDS, MS_TO_US(1), NULL); 353 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 354 err(1, "%s", test_id); 355 356 /* Wait for 2 ms to give the timer plenty of time to expire. */ 357 mssleep(2); 358 359 /* Now re-add the timer */ 360 start = now(); 361 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 362 err(1, "%s", test_id); 363 364 /* Wait for the event */ 365 kev.flags |= EV_CLEAR; 366 kev.fflags &= ~NOTE_USECONDS; 367 kev.data = 1; 368 kevent_cmp(&kev, kevent_get(kqfd)); 369 elapsed = now() - start; 370 371 /* Check that the timer expired after at least 1 ms. This check 372 * is to make sure that the timer re-started and that the event is 373 * not from the original add (and expiration) of the timer. 374 */ 375 printf("timer expired after %ld us\n", elapsed); 376 if (elapsed < MS_TO_US(1)) 377 errx(1, "early timer expiration: %ld us", elapsed); 378 379 /* Make sure the re-added timer does not fire. In other words, 380 * test that the event received above was the only event from the 381 * add and re-add of the timer. 382 */ 383 mssleep(2); 384 test_no_kevents(); 385 386 success(); 387 } 388 389 static void 390 test_update_periodic(void) 391 { 392 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)"; 393 struct kevent kev; 394 long elapsed; 395 long start; 396 long stop; 397 398 test_begin(test_id); 399 400 test_no_kevents(); 401 402 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL); 403 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 404 err(1, "%s", test_id); 405 406 /* Retrieve the event */ 407 kev.flags = EV_ADD | EV_CLEAR; 408 kev.data = 1; 409 kevent_cmp(&kev, kevent_get(kqfd)); 410 411 /* Check if the event occurs again */ 412 sleep(1); 413 kevent_cmp(&kev, kevent_get(kqfd)); 414 415 /* Re-add with new timeout. */ 416 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL); 417 start = now(); 418 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 419 err(1, "%s", test_id); 420 421 /* Retrieve the event */ 422 kev.flags = EV_ADD | EV_CLEAR; 423 kev.data = 1; 424 kevent_cmp(&kev, kevent_get(kqfd)); 425 426 stop = now(); 427 elapsed = stop - start; 428 429 /* Check that the timer expired after at least 2 ms. 430 */ 431 printf("timer expired after %ld us\n", elapsed); 432 if (elapsed < MS_TO_US(2)) 433 errx(1, "early timer expiration: %ld us", elapsed); 434 435 /* Delete the event */ 436 kev.flags = EV_DELETE; 437 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 438 err(1, "%s", test_id); 439 440 success(); 441 } 442 443 static void 444 test_update_timing(void) 445 { 446 #define MIN_SLEEP 500 447 #define MAX_SLEEP 1500 448 const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)"; 449 struct kevent kev; 450 int iteration; 451 int sleeptime; 452 long elapsed; 453 long start; 454 long stop; 455 456 test_begin(test_id); 457 458 test_no_kevents(); 459 460 /* Re-try the update tests with a variety of delays between the 461 * original timer activation and the update of the timer. The goal 462 * is to show that in all cases the only timer event that is 463 * received is from the update and not the original timer add. 464 */ 465 for (sleeptime = MIN_SLEEP, iteration = 1; 466 sleeptime < MAX_SLEEP; 467 ++sleeptime, ++iteration) { 468 469 /* First set the timer to 1 ms */ 470 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 471 NOTE_USECONDS, MS_TO_US(1), NULL); 472 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 473 err(1, "%s", test_id); 474 475 /* Delay; the delay ranges from less than to greater than the 476 * timer period. 477 */ 478 ussleep(sleeptime); 479 480 /* Now re-add the timer with the same parameters */ 481 start = now(); 482 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 483 err(1, "%s", test_id); 484 485 /* Wait for the event */ 486 kev.flags |= EV_CLEAR; 487 kev.fflags &= ~NOTE_USECONDS; 488 kev.data = 1; 489 kevent_cmp(&kev, kevent_get(kqfd)); 490 stop = now(); 491 elapsed = stop - start; 492 493 /* Check that the timer expired after at least 1 ms. This 494 * check is to make sure that the timer re-started and that 495 * the event is not from the original add of the timer. 496 */ 497 if (elapsed < MS_TO_US(1)) 498 errx(1, "early timer expiration: %ld us", elapsed); 499 500 /* Make sure the re-added timer does not fire. In other words, 501 * test that the event received above was the only event from 502 * the add and re-add of the timer. 503 */ 504 mssleep(2); 505 test_no_kevents_quietly(); 506 } 507 508 success(); 509 } 510 511 void 512 test_evfilt_timer() 513 { 514 kqfd = kqueue(); 515 test_kevent_timer_add(); 516 test_kevent_timer_del(); 517 test_kevent_timer_get(); 518 test_oneshot(); 519 test_periodic(); 520 test_abstime(); 521 test_update(); 522 test_update_equal(); 523 test_update_expired(); 524 test_update_timing(); 525 test_update_periodic(); 526 disable_and_enable(); 527 close(kqfd); 528 } 529