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