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 uint64_t 34 now(void) 35 { 36 struct timeval tv; 37 38 gettimeofday(&tv, NULL); 39 /* Promote potentially 32-bit time_t to uint64_t before conversion. */ 40 return SEC_TO_US((uint64_t)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 static 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 static 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 static 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 static 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 static 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 uint64_t end, start, stop; 221 const int timeout_sec = 3; 222 223 test_begin(test_id); 224 225 test_no_kevents(); 226 227 start = now(); 228 end = start + SEC_TO_US(timeout_sec); 229 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 230 NOTE_ABSTIME | NOTE_USECONDS, end, 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 240 stop = now(); 241 if (stop < end) 242 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end); 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 uint64_t 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 uint64_t 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 uint64_t 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 uint64_t start, 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 uint64_t start, stop; 454 455 test_begin(test_id); 456 457 test_no_kevents(); 458 459 /* Re-try the update tests with a variety of delays between the 460 * original timer activation and the update of the timer. The goal 461 * is to show that in all cases the only timer event that is 462 * received is from the update and not the original timer add. 463 */ 464 for (sleeptime = MIN_SLEEP, iteration = 1; 465 sleeptime < MAX_SLEEP; 466 ++sleeptime, ++iteration) { 467 468 /* First set the timer to 1 ms */ 469 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 470 NOTE_USECONDS, MS_TO_US(1), NULL); 471 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 472 err(1, "%s", test_id); 473 474 /* Delay; the delay ranges from less than to greater than the 475 * timer period. 476 */ 477 ussleep(sleeptime); 478 479 /* Now re-add the timer with the same parameters */ 480 start = now(); 481 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 482 err(1, "%s", test_id); 483 484 /* Wait for the event */ 485 kev.flags |= EV_CLEAR; 486 kev.fflags &= ~NOTE_USECONDS; 487 kev.data = 1; 488 kevent_cmp(&kev, kevent_get(kqfd)); 489 stop = now(); 490 elapsed = stop - start; 491 492 /* Check that the timer expired after at least 1 ms. This 493 * check is to make sure that the timer re-started and that 494 * the event is not from the original add of the timer. 495 */ 496 if (elapsed < MS_TO_US(1)) 497 errx(1, "early timer expiration: %ld us", elapsed); 498 499 /* Make sure the re-added timer does not fire. In other words, 500 * test that the event received above was the only event from 501 * the add and re-add of the timer. 502 */ 503 mssleep(2); 504 test_no_kevents_quietly(); 505 } 506 507 success(); 508 } 509 510 void 511 test_evfilt_timer(void) 512 { 513 kqfd = kqueue(); 514 test_kevent_timer_add(); 515 test_kevent_timer_del(); 516 test_kevent_timer_get(); 517 test_oneshot(); 518 test_periodic(); 519 test_abstime(); 520 test_update(); 521 test_update_equal(); 522 test_update_expired(); 523 test_update_timing(); 524 test_update_periodic(); 525 disable_and_enable(); 526 close(kqfd); 527 } 528