1 /* $NetBSD: timer.c,v 1.10 2014/12/10 04:37:59 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004, 2005, 2007-2009, 2011-2014 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1998-2002 Internet Software Consortium. 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id */ 21 22 /*! \file */ 23 24 #include <config.h> 25 26 #include <isc/app.h> 27 #include <isc/condition.h> 28 #include <isc/heap.h> 29 #include <isc/log.h> 30 #include <isc/magic.h> 31 #include <isc/mem.h> 32 #include <isc/msgs.h> 33 #include <isc/once.h> 34 #include <isc/platform.h> 35 #include <isc/task.h> 36 #include <isc/thread.h> 37 #include <isc/time.h> 38 #include <isc/timer.h> 39 #include <isc/util.h> 40 41 #ifdef OPENSSL_LEAKS 42 #include <openssl/err.h> 43 #endif 44 45 /* See task.c about the following definition: */ 46 #ifdef ISC_PLATFORM_USETHREADS 47 #define USE_TIMER_THREAD 48 #else 49 #define USE_SHARED_MANAGER 50 #endif /* ISC_PLATFORM_USETHREADS */ 51 52 #ifndef USE_TIMER_THREAD 53 #include "timer_p.h" 54 #endif /* USE_TIMER_THREAD */ 55 56 #ifdef ISC_TIMER_TRACE 57 #define XTRACE(s) fprintf(stderr, "%s\n", (s)) 58 #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t)) 59 #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \ 60 (d).seconds, (d).nanoseconds) 61 #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \ 62 (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds) 63 #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \ 64 (d).seconds, (d).nanoseconds) 65 #else 66 #define XTRACE(s) 67 #define XTRACEID(s, t) 68 #define XTRACETIME(s, d) 69 #define XTRACETIME2(s, d, n) 70 #define XTRACETIMER(s, t, d) 71 #endif /* ISC_TIMER_TRACE */ 72 73 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R') 74 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC) 75 76 typedef struct isc__timer isc__timer_t; 77 typedef struct isc__timermgr isc__timermgr_t; 78 79 struct isc__timer { 80 /*! Not locked. */ 81 isc_timer_t common; 82 isc__timermgr_t * manager; 83 isc_mutex_t lock; 84 /*! Locked by timer lock. */ 85 unsigned int references; 86 isc_time_t idle; 87 /*! Locked by manager lock. */ 88 isc_timertype_t type; 89 isc_time_t expires; 90 isc_interval_t interval; 91 isc_task_t * task; 92 isc_taskaction_t action; 93 void * arg; 94 unsigned int index; 95 isc_time_t due; 96 LINK(isc__timer_t) link; 97 }; 98 99 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M') 100 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC) 101 102 struct isc__timermgr { 103 /* Not locked. */ 104 isc_timermgr_t common; 105 isc_mem_t * mctx; 106 isc_mutex_t lock; 107 /* Locked by manager lock. */ 108 isc_boolean_t done; 109 LIST(isc__timer_t) timers; 110 unsigned int nscheduled; 111 isc_time_t due; 112 #ifdef USE_TIMER_THREAD 113 isc_condition_t wakeup; 114 isc_thread_t thread; 115 #endif /* USE_TIMER_THREAD */ 116 #ifdef USE_SHARED_MANAGER 117 unsigned int refs; 118 #endif /* USE_SHARED_MANAGER */ 119 isc_heap_t * heap; 120 }; 121 122 /*% 123 * The following are intended for internal use (indicated by "isc__" 124 * prefix) but are not declared as static, allowing direct access from 125 * unit tests etc. 126 */ 127 128 isc_result_t 129 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type, 130 const isc_time_t *expires, const isc_interval_t *interval, 131 isc_task_t *task, isc_taskaction_t action, void *arg, 132 isc_timer_t **timerp); 133 isc_result_t 134 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type, 135 const isc_time_t *expires, const isc_interval_t *interval, 136 isc_boolean_t purge); 137 isc_timertype_t 138 isc_timer_gettype(isc_timer_t *timer); 139 isc_result_t 140 isc__timer_touch(isc_timer_t *timer); 141 void 142 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp); 143 void 144 isc__timer_detach(isc_timer_t **timerp); 145 isc_result_t 146 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp); 147 void 148 isc_timermgr_poke(isc_timermgr_t *manager0); 149 void 150 isc__timermgr_destroy(isc_timermgr_t **managerp); 151 152 static struct isc__timermethods { 153 isc_timermethods_t methods; 154 155 /*% 156 * The following are defined just for avoiding unused static functions. 157 */ 158 void *gettype; 159 } timermethods = { 160 { 161 isc__timer_attach, 162 isc__timer_detach, 163 isc__timer_reset, 164 isc__timer_touch 165 }, 166 (void *)isc_timer_gettype 167 }; 168 169 static struct isc__timermgrmethods { 170 isc_timermgrmethods_t methods; 171 void *poke; /* see above */ 172 } timermgrmethods = { 173 { 174 isc__timermgr_destroy, 175 isc__timer_create 176 }, 177 (void *)isc_timermgr_poke 178 }; 179 180 #ifdef USE_SHARED_MANAGER 181 /*! 182 * If the manager is supposed to be shared, there can be only one. 183 */ 184 static isc__timermgr_t *timermgr = NULL; 185 #endif /* USE_SHARED_MANAGER */ 186 187 static inline isc_result_t 188 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) { 189 isc_result_t result; 190 isc__timermgr_t *manager; 191 isc_time_t due; 192 int cmp; 193 #ifdef USE_TIMER_THREAD 194 isc_boolean_t timedwait; 195 #endif 196 197 /*! 198 * Note: the caller must ensure locking. 199 */ 200 201 REQUIRE(timer->type != isc_timertype_inactive); 202 203 #ifndef USE_TIMER_THREAD 204 UNUSED(signal_ok); 205 #endif /* USE_TIMER_THREAD */ 206 207 manager = timer->manager; 208 209 #ifdef USE_TIMER_THREAD 210 /*! 211 * If the manager was timed wait, we may need to signal the 212 * manager to force a wakeup. 213 */ 214 timedwait = ISC_TF(manager->nscheduled > 0 && 215 isc_time_seconds(&manager->due) != 0); 216 #endif 217 218 /* 219 * Compute the new due time. 220 */ 221 if (timer->type != isc_timertype_once) { 222 result = isc_time_add(now, &timer->interval, &due); 223 if (result != ISC_R_SUCCESS) 224 return (result); 225 if (timer->type == isc_timertype_limited && 226 isc_time_compare(&timer->expires, &due) < 0) 227 due = timer->expires; 228 } else { 229 if (isc_time_isepoch(&timer->idle)) 230 due = timer->expires; 231 else if (isc_time_isepoch(&timer->expires)) 232 due = timer->idle; 233 else if (isc_time_compare(&timer->idle, &timer->expires) < 0) 234 due = timer->idle; 235 else 236 due = timer->expires; 237 } 238 239 /* 240 * Schedule the timer. 241 */ 242 243 if (timer->index > 0) { 244 /* 245 * Already scheduled. 246 */ 247 cmp = isc_time_compare(&due, &timer->due); 248 timer->due = due; 249 switch (cmp) { 250 case -1: 251 isc_heap_increased(manager->heap, timer->index); 252 break; 253 case 1: 254 isc_heap_decreased(manager->heap, timer->index); 255 break; 256 case 0: 257 /* Nothing to do. */ 258 break; 259 } 260 } else { 261 timer->due = due; 262 result = isc_heap_insert(manager->heap, timer); 263 if (result != ISC_R_SUCCESS) { 264 INSIST(result == ISC_R_NOMEMORY); 265 return (ISC_R_NOMEMORY); 266 } 267 manager->nscheduled++; 268 } 269 270 XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 271 ISC_MSG_SCHEDULE, "schedule"), timer, due); 272 273 /* 274 * If this timer is at the head of the queue, we need to ensure 275 * that we won't miss it if it has a more recent due time than 276 * the current "next" timer. We do this either by waking up the 277 * run thread, or explicitly setting the value in the manager. 278 */ 279 #ifdef USE_TIMER_THREAD 280 281 /* 282 * This is a temporary (probably) hack to fix a bug on tru64 5.1 283 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually 284 * return when the time expires, so here, we check to see if 285 * we're 15 seconds or more behind, and if we are, we signal 286 * the dispatcher. This isn't such a bad idea as a general purpose 287 * watchdog, so perhaps we should just leave it in here. 288 */ 289 if (signal_ok && timedwait) { 290 isc_interval_t fifteen; 291 isc_time_t then; 292 293 isc_interval_set(&fifteen, 15, 0); 294 result = isc_time_add(&manager->due, &fifteen, &then); 295 296 if (result == ISC_R_SUCCESS && 297 isc_time_compare(&then, now) < 0) { 298 SIGNAL(&manager->wakeup); 299 signal_ok = ISC_FALSE; 300 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, 301 ISC_LOGMODULE_TIMER, ISC_LOG_WARNING, 302 "*** POKED TIMER ***"); 303 } 304 } 305 306 if (timer->index == 1 && signal_ok) { 307 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 308 ISC_MSG_SIGNALSCHED, 309 "signal (schedule)")); 310 SIGNAL(&manager->wakeup); 311 } 312 #else /* USE_TIMER_THREAD */ 313 if (timer->index == 1 && 314 isc_time_compare(&timer->due, &manager->due) < 0) 315 manager->due = timer->due; 316 #endif /* USE_TIMER_THREAD */ 317 318 return (ISC_R_SUCCESS); 319 } 320 321 static inline void 322 deschedule(isc__timer_t *timer) { 323 #ifdef USE_TIMER_THREAD 324 isc_boolean_t need_wakeup = ISC_FALSE; 325 #endif 326 isc__timermgr_t *manager; 327 328 /* 329 * The caller must ensure locking. 330 */ 331 332 manager = timer->manager; 333 if (timer->index > 0) { 334 #ifdef USE_TIMER_THREAD 335 if (timer->index == 1) 336 need_wakeup = ISC_TRUE; 337 #endif 338 isc_heap_delete(manager->heap, timer->index); 339 timer->index = 0; 340 INSIST(manager->nscheduled > 0); 341 manager->nscheduled--; 342 #ifdef USE_TIMER_THREAD 343 if (need_wakeup) { 344 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 345 ISC_MSG_SIGNALDESCHED, 346 "signal (deschedule)")); 347 SIGNAL(&manager->wakeup); 348 } 349 #endif /* USE_TIMER_THREAD */ 350 } 351 } 352 353 static void 354 destroy(isc__timer_t *timer) { 355 isc__timermgr_t *manager = timer->manager; 356 357 /* 358 * The caller must ensure it is safe to destroy the timer. 359 */ 360 361 LOCK(&manager->lock); 362 363 (void)isc_task_purgerange(timer->task, 364 timer, 365 ISC_TIMEREVENT_FIRSTEVENT, 366 ISC_TIMEREVENT_LASTEVENT, 367 NULL); 368 deschedule(timer); 369 UNLINK(manager->timers, timer, link); 370 371 UNLOCK(&manager->lock); 372 373 isc_task_detach(&timer->task); 374 DESTROYLOCK(&timer->lock); 375 timer->common.impmagic = 0; 376 timer->common.magic = 0; 377 isc_mem_put(manager->mctx, timer, sizeof(*timer)); 378 } 379 380 isc_result_t 381 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type, 382 const isc_time_t *expires, const isc_interval_t *interval, 383 isc_task_t *task, isc_taskaction_t action, void *arg, 384 isc_timer_t **timerp) 385 { 386 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 387 isc__timer_t *timer; 388 isc_result_t result; 389 isc_time_t now; 390 391 /* 392 * Create a new 'type' timer managed by 'manager'. The timers 393 * parameters are specified by 'expires' and 'interval'. Events 394 * will be posted to 'task' and when dispatched 'action' will be 395 * called with 'arg' as the arg value. The new timer is returned 396 * in 'timerp'. 397 */ 398 399 REQUIRE(VALID_MANAGER(manager)); 400 REQUIRE(task != NULL); 401 REQUIRE(action != NULL); 402 if (expires == NULL) 403 expires = isc_time_epoch; 404 if (interval == NULL) 405 interval = isc_interval_zero; 406 REQUIRE(type == isc_timertype_inactive || 407 !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); 408 REQUIRE(timerp != NULL && *timerp == NULL); 409 REQUIRE(type != isc_timertype_limited || 410 !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); 411 412 /* 413 * Get current time. 414 */ 415 if (type != isc_timertype_inactive) { 416 TIME_NOW(&now); 417 } else { 418 /* 419 * We don't have to do this, but it keeps the compiler from 420 * complaining about "now" possibly being used without being 421 * set, even though it will never actually happen. 422 */ 423 isc_time_settoepoch(&now); 424 } 425 426 427 timer = isc_mem_get(manager->mctx, sizeof(*timer)); 428 if (timer == NULL) 429 return (ISC_R_NOMEMORY); 430 431 timer->manager = manager; 432 timer->references = 1; 433 434 if (type == isc_timertype_once && !isc_interval_iszero(interval)) { 435 result = isc_time_add(&now, interval, &timer->idle); 436 if (result != ISC_R_SUCCESS) { 437 isc_mem_put(manager->mctx, timer, sizeof(*timer)); 438 return (result); 439 } 440 } else 441 isc_time_settoepoch(&timer->idle); 442 443 timer->type = type; 444 timer->expires = *expires; 445 timer->interval = *interval; 446 timer->task = NULL; 447 isc_task_attach(task, &timer->task); 448 timer->action = action; 449 /* 450 * Removing the const attribute from "arg" is the best of two 451 * evils here. If the timer->arg member is made const, then 452 * it affects a great many recipients of the timer event 453 * which did not pass in an "arg" that was truly const. 454 * Changing isc_timer_create() to not have "arg" prototyped as const, 455 * though, can cause compilers warnings for calls that *do* 456 * have a truly const arg. The caller will have to carefully 457 * keep track of whether arg started as a true const. 458 */ 459 DE_CONST(arg, timer->arg); 460 timer->index = 0; 461 result = isc_mutex_init(&timer->lock); 462 if (result != ISC_R_SUCCESS) { 463 isc_task_detach(&timer->task); 464 isc_mem_put(manager->mctx, timer, sizeof(*timer)); 465 return (result); 466 } 467 ISC_LINK_INIT(timer, link); 468 timer->common.impmagic = TIMER_MAGIC; 469 timer->common.magic = ISCAPI_TIMER_MAGIC; 470 timer->common.methods = (isc_timermethods_t *)&timermethods; 471 472 LOCK(&manager->lock); 473 474 /* 475 * Note we don't have to lock the timer like we normally would because 476 * there are no external references to it yet. 477 */ 478 479 if (type != isc_timertype_inactive) 480 result = schedule(timer, &now, ISC_TRUE); 481 else 482 result = ISC_R_SUCCESS; 483 if (result == ISC_R_SUCCESS) 484 APPEND(manager->timers, timer, link); 485 486 UNLOCK(&manager->lock); 487 488 if (result != ISC_R_SUCCESS) { 489 timer->common.impmagic = 0; 490 timer->common.magic = 0; 491 DESTROYLOCK(&timer->lock); 492 isc_task_detach(&timer->task); 493 isc_mem_put(manager->mctx, timer, sizeof(*timer)); 494 return (result); 495 } 496 497 *timerp = (isc_timer_t *)timer; 498 499 return (ISC_R_SUCCESS); 500 } 501 502 isc_result_t 503 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type, 504 const isc_time_t *expires, const isc_interval_t *interval, 505 isc_boolean_t purge) 506 { 507 isc__timer_t *timer = (isc__timer_t *)timer0; 508 isc_time_t now; 509 isc__timermgr_t *manager; 510 isc_result_t result; 511 512 /* 513 * Change the timer's type, expires, and interval values to the given 514 * values. If 'purge' is ISC_TRUE, any pending events from this timer 515 * are purged from its task's event queue. 516 */ 517 518 REQUIRE(VALID_TIMER(timer)); 519 manager = timer->manager; 520 REQUIRE(VALID_MANAGER(manager)); 521 522 if (expires == NULL) 523 expires = isc_time_epoch; 524 if (interval == NULL) 525 interval = isc_interval_zero; 526 REQUIRE(type == isc_timertype_inactive || 527 !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); 528 REQUIRE(type != isc_timertype_limited || 529 !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); 530 531 /* 532 * Get current time. 533 */ 534 if (type != isc_timertype_inactive) { 535 TIME_NOW(&now); 536 } else { 537 /* 538 * We don't have to do this, but it keeps the compiler from 539 * complaining about "now" possibly being used without being 540 * set, even though it will never actually happen. 541 */ 542 isc_time_settoepoch(&now); 543 } 544 545 LOCK(&manager->lock); 546 LOCK(&timer->lock); 547 548 if (purge) 549 (void)isc_task_purgerange(timer->task, 550 timer, 551 ISC_TIMEREVENT_FIRSTEVENT, 552 ISC_TIMEREVENT_LASTEVENT, 553 NULL); 554 timer->type = type; 555 timer->expires = *expires; 556 timer->interval = *interval; 557 if (type == isc_timertype_once && !isc_interval_iszero(interval)) { 558 result = isc_time_add(&now, interval, &timer->idle); 559 } else { 560 isc_time_settoepoch(&timer->idle); 561 result = ISC_R_SUCCESS; 562 } 563 564 if (result == ISC_R_SUCCESS) { 565 if (type == isc_timertype_inactive) { 566 deschedule(timer); 567 result = ISC_R_SUCCESS; 568 } else 569 result = schedule(timer, &now, ISC_TRUE); 570 } 571 572 UNLOCK(&timer->lock); 573 UNLOCK(&manager->lock); 574 575 return (result); 576 } 577 578 isc_timertype_t 579 isc_timer_gettype(isc_timer_t *timer0) { 580 isc__timer_t *timer = (isc__timer_t *)timer0; 581 isc_timertype_t t; 582 583 REQUIRE(VALID_TIMER(timer)); 584 585 LOCK(&timer->lock); 586 t = timer->type; 587 UNLOCK(&timer->lock); 588 589 return (t); 590 } 591 592 isc_result_t 593 isc__timer_touch(isc_timer_t *timer0) { 594 isc__timer_t *timer = (isc__timer_t *)timer0; 595 isc_result_t result; 596 isc_time_t now; 597 598 /* 599 * Set the last-touched time of 'timer' to the current time. 600 */ 601 602 REQUIRE(VALID_TIMER(timer)); 603 604 LOCK(&timer->lock); 605 606 /* 607 * We'd like to 608 * 609 * REQUIRE(timer->type == isc_timertype_once); 610 * 611 * but we cannot without locking the manager lock too, which we 612 * don't want to do. 613 */ 614 615 TIME_NOW(&now); 616 result = isc_time_add(&now, &timer->interval, &timer->idle); 617 618 UNLOCK(&timer->lock); 619 620 return (result); 621 } 622 623 void 624 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) { 625 isc__timer_t *timer = (isc__timer_t *)timer0; 626 627 /* 628 * Attach *timerp to timer. 629 */ 630 631 REQUIRE(VALID_TIMER(timer)); 632 REQUIRE(timerp != NULL && *timerp == NULL); 633 634 LOCK(&timer->lock); 635 timer->references++; 636 UNLOCK(&timer->lock); 637 638 *timerp = (isc_timer_t *)timer; 639 } 640 641 void 642 isc__timer_detach(isc_timer_t **timerp) { 643 isc__timer_t *timer; 644 isc_boolean_t free_timer = ISC_FALSE; 645 646 /* 647 * Detach *timerp from its timer. 648 */ 649 650 REQUIRE(timerp != NULL); 651 timer = (isc__timer_t *)*timerp; 652 REQUIRE(VALID_TIMER(timer)); 653 654 LOCK(&timer->lock); 655 REQUIRE(timer->references > 0); 656 timer->references--; 657 if (timer->references == 0) 658 free_timer = ISC_TRUE; 659 UNLOCK(&timer->lock); 660 661 if (free_timer) 662 destroy(timer); 663 664 *timerp = NULL; 665 } 666 667 static void 668 dispatch(isc__timermgr_t *manager, isc_time_t *now) { 669 isc_boolean_t done = ISC_FALSE, post_event, need_schedule; 670 isc_timerevent_t *event; 671 isc_eventtype_t type = 0; 672 isc__timer_t *timer; 673 isc_result_t result; 674 isc_boolean_t idle; 675 676 /*! 677 * The caller must be holding the manager lock. 678 */ 679 680 while (manager->nscheduled > 0 && !done) { 681 timer = isc_heap_element(manager->heap, 1); 682 INSIST(timer != NULL && timer->type != isc_timertype_inactive); 683 if (isc_time_compare(now, &timer->due) >= 0) { 684 if (timer->type == isc_timertype_ticker) { 685 type = ISC_TIMEREVENT_TICK; 686 post_event = ISC_TRUE; 687 need_schedule = ISC_TRUE; 688 } else if (timer->type == isc_timertype_limited) { 689 int cmp; 690 cmp = isc_time_compare(now, &timer->expires); 691 if (cmp >= 0) { 692 type = ISC_TIMEREVENT_LIFE; 693 post_event = ISC_TRUE; 694 need_schedule = ISC_FALSE; 695 } else { 696 type = ISC_TIMEREVENT_TICK; 697 post_event = ISC_TRUE; 698 need_schedule = ISC_TRUE; 699 } 700 } else if (!isc_time_isepoch(&timer->expires) && 701 isc_time_compare(now, 702 &timer->expires) >= 0) { 703 type = ISC_TIMEREVENT_LIFE; 704 post_event = ISC_TRUE; 705 need_schedule = ISC_FALSE; 706 } else { 707 idle = ISC_FALSE; 708 709 LOCK(&timer->lock); 710 if (!isc_time_isepoch(&timer->idle) && 711 isc_time_compare(now, 712 &timer->idle) >= 0) { 713 idle = ISC_TRUE; 714 } 715 UNLOCK(&timer->lock); 716 if (idle) { 717 type = ISC_TIMEREVENT_IDLE; 718 post_event = ISC_TRUE; 719 need_schedule = ISC_FALSE; 720 } else { 721 /* 722 * Idle timer has been touched; 723 * reschedule. 724 */ 725 XTRACEID(isc_msgcat_get(isc_msgcat, 726 ISC_MSGSET_TIMER, 727 ISC_MSG_IDLERESCHED, 728 "idle reschedule"), 729 timer); 730 post_event = ISC_FALSE; 731 need_schedule = ISC_TRUE; 732 } 733 } 734 735 if (post_event) { 736 XTRACEID(isc_msgcat_get(isc_msgcat, 737 ISC_MSGSET_TIMER, 738 ISC_MSG_POSTING, 739 "posting"), timer); 740 /* 741 * XXX We could preallocate this event. 742 */ 743 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx, 744 timer, 745 type, 746 timer->action, 747 timer->arg, 748 sizeof(*event)); 749 750 if (event != NULL) { 751 event->due = timer->due; 752 isc_task_send(timer->task, 753 ISC_EVENT_PTR(&event)); 754 } else 755 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", 756 isc_msgcat_get(isc_msgcat, 757 ISC_MSGSET_TIMER, 758 ISC_MSG_EVENTNOTALLOC, 759 "couldn't " 760 "allocate event")); 761 } 762 763 timer->index = 0; 764 isc_heap_delete(manager->heap, 1); 765 manager->nscheduled--; 766 767 if (need_schedule) { 768 result = schedule(timer, now, ISC_FALSE); 769 if (result != ISC_R_SUCCESS) 770 UNEXPECTED_ERROR(__FILE__, __LINE__, 771 "%s: %u", 772 isc_msgcat_get(isc_msgcat, 773 ISC_MSGSET_TIMER, 774 ISC_MSG_SCHEDFAIL, 775 "couldn't schedule " 776 "timer"), 777 result); 778 } 779 } else { 780 manager->due = timer->due; 781 done = ISC_TRUE; 782 } 783 } 784 } 785 786 #ifdef USE_TIMER_THREAD 787 static isc_threadresult_t 788 #ifdef _WIN32 /* XXXDCL */ 789 WINAPI 790 #endif 791 run(void *uap) { 792 isc__timermgr_t *manager = uap; 793 isc_time_t now; 794 isc_result_t result; 795 796 LOCK(&manager->lock); 797 while (!manager->done) { 798 TIME_NOW(&now); 799 800 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 801 ISC_MSG_RUNNING, 802 "running"), now); 803 804 dispatch(manager, &now); 805 806 if (manager->nscheduled > 0) { 807 XTRACETIME2(isc_msgcat_get(isc_msgcat, 808 ISC_MSGSET_GENERAL, 809 ISC_MSG_WAITUNTIL, 810 "waituntil"), 811 manager->due, now); 812 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due); 813 INSIST(result == ISC_R_SUCCESS || 814 result == ISC_R_TIMEDOUT); 815 } else { 816 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 817 ISC_MSG_WAIT, "wait"), now); 818 WAIT(&manager->wakeup, &manager->lock); 819 } 820 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 821 ISC_MSG_WAKEUP, "wakeup")); 822 } 823 UNLOCK(&manager->lock); 824 825 #ifdef OPENSSL_LEAKS 826 ERR_remove_state(0); 827 #endif 828 829 return ((isc_threadresult_t)0); 830 } 831 #endif /* USE_TIMER_THREAD */ 832 833 static isc_boolean_t 834 sooner(void *v1, void *v2) { 835 isc__timer_t *t1, *t2; 836 837 t1 = v1; 838 t2 = v2; 839 REQUIRE(VALID_TIMER(t1)); 840 REQUIRE(VALID_TIMER(t2)); 841 842 if (isc_time_compare(&t1->due, &t2->due) < 0) 843 return (ISC_TRUE); 844 return (ISC_FALSE); 845 } 846 847 static void 848 set_index(void *what, unsigned int index) { 849 isc__timer_t *timer; 850 851 timer = what; 852 REQUIRE(VALID_TIMER(timer)); 853 854 timer->index = index; 855 } 856 857 isc_result_t 858 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { 859 isc__timermgr_t *manager; 860 isc_result_t result; 861 862 /* 863 * Create a timer manager. 864 */ 865 866 REQUIRE(managerp != NULL && *managerp == NULL); 867 868 #ifdef USE_SHARED_MANAGER 869 if (timermgr != NULL) { 870 timermgr->refs++; 871 *managerp = (isc_timermgr_t *)timermgr; 872 return (ISC_R_SUCCESS); 873 } 874 #endif /* USE_SHARED_MANAGER */ 875 876 manager = isc_mem_get(mctx, sizeof(*manager)); 877 if (manager == NULL) 878 return (ISC_R_NOMEMORY); 879 880 manager->common.impmagic = TIMER_MANAGER_MAGIC; 881 manager->common.magic = ISCAPI_TIMERMGR_MAGIC; 882 manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods; 883 manager->mctx = NULL; 884 manager->done = ISC_FALSE; 885 INIT_LIST(manager->timers); 886 manager->nscheduled = 0; 887 isc_time_settoepoch(&manager->due); 888 manager->heap = NULL; 889 result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap); 890 if (result != ISC_R_SUCCESS) { 891 INSIST(result == ISC_R_NOMEMORY); 892 isc_mem_put(mctx, manager, sizeof(*manager)); 893 return (ISC_R_NOMEMORY); 894 } 895 result = isc_mutex_init(&manager->lock); 896 if (result != ISC_R_SUCCESS) { 897 isc_heap_destroy(&manager->heap); 898 isc_mem_put(mctx, manager, sizeof(*manager)); 899 return (result); 900 } 901 isc_mem_attach(mctx, &manager->mctx); 902 #ifdef USE_TIMER_THREAD 903 if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) { 904 isc_mem_detach(&manager->mctx); 905 DESTROYLOCK(&manager->lock); 906 isc_heap_destroy(&manager->heap); 907 isc_mem_put(mctx, manager, sizeof(*manager)); 908 UNEXPECTED_ERROR(__FILE__, __LINE__, 909 "isc_condition_init() %s", 910 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 911 ISC_MSG_FAILED, "failed")); 912 return (ISC_R_UNEXPECTED); 913 } 914 if (isc_thread_create(run, manager, &manager->thread) != 915 ISC_R_SUCCESS) { 916 isc_mem_detach(&manager->mctx); 917 (void)isc_condition_destroy(&manager->wakeup); 918 DESTROYLOCK(&manager->lock); 919 isc_heap_destroy(&manager->heap); 920 isc_mem_put(mctx, manager, sizeof(*manager)); 921 UNEXPECTED_ERROR(__FILE__, __LINE__, 922 "isc_thread_create() %s", 923 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 924 ISC_MSG_FAILED, "failed")); 925 return (ISC_R_UNEXPECTED); 926 } 927 #endif 928 #ifdef USE_SHARED_MANAGER 929 manager->refs = 1; 930 timermgr = manager; 931 #endif /* USE_SHARED_MANAGER */ 932 933 *managerp = (isc_timermgr_t *)manager; 934 935 return (ISC_R_SUCCESS); 936 } 937 938 void 939 isc_timermgr_poke(isc_timermgr_t *manager0) { 940 #ifdef USE_TIMER_THREAD 941 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 942 943 REQUIRE(VALID_MANAGER(manager)); 944 945 SIGNAL(&manager->wakeup); 946 #else 947 UNUSED(manager0); 948 #endif 949 } 950 951 void 952 isc__timermgr_destroy(isc_timermgr_t **managerp) { 953 isc__timermgr_t *manager; 954 isc_mem_t *mctx; 955 956 /* 957 * Destroy a timer manager. 958 */ 959 960 REQUIRE(managerp != NULL); 961 manager = (isc__timermgr_t *)*managerp; 962 REQUIRE(VALID_MANAGER(manager)); 963 964 LOCK(&manager->lock); 965 966 #ifdef USE_SHARED_MANAGER 967 manager->refs--; 968 if (manager->refs > 0) { 969 UNLOCK(&manager->lock); 970 *managerp = NULL; 971 return; 972 } 973 timermgr = NULL; 974 #endif /* USE_SHARED_MANAGER */ 975 976 #ifndef USE_TIMER_THREAD 977 isc__timermgr_dispatch((isc_timermgr_t *)manager); 978 #endif 979 980 REQUIRE(EMPTY(manager->timers)); 981 manager->done = ISC_TRUE; 982 983 #ifdef USE_TIMER_THREAD 984 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 985 ISC_MSG_SIGNALDESTROY, "signal (destroy)")); 986 SIGNAL(&manager->wakeup); 987 #endif /* USE_TIMER_THREAD */ 988 989 UNLOCK(&manager->lock); 990 991 #ifdef USE_TIMER_THREAD 992 /* 993 * Wait for thread to exit. 994 */ 995 if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS) 996 UNEXPECTED_ERROR(__FILE__, __LINE__, 997 "isc_thread_join() %s", 998 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 999 ISC_MSG_FAILED, "failed")); 1000 #endif /* USE_TIMER_THREAD */ 1001 1002 /* 1003 * Clean up. 1004 */ 1005 #ifdef USE_TIMER_THREAD 1006 (void)isc_condition_destroy(&manager->wakeup); 1007 #endif /* USE_TIMER_THREAD */ 1008 DESTROYLOCK(&manager->lock); 1009 isc_heap_destroy(&manager->heap); 1010 manager->common.impmagic = 0; 1011 manager->common.magic = 0; 1012 mctx = manager->mctx; 1013 isc_mem_put(mctx, manager, sizeof(*manager)); 1014 isc_mem_detach(&mctx); 1015 1016 *managerp = NULL; 1017 1018 #ifdef USE_SHARED_MANAGER 1019 timermgr = NULL; 1020 #endif 1021 } 1022 1023 #ifndef USE_TIMER_THREAD 1024 isc_result_t 1025 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) { 1026 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 1027 1028 #ifdef USE_SHARED_MANAGER 1029 if (manager == NULL) 1030 manager = timermgr; 1031 #endif 1032 if (manager == NULL || manager->nscheduled == 0) 1033 return (ISC_R_NOTFOUND); 1034 *when = manager->due; 1035 return (ISC_R_SUCCESS); 1036 } 1037 1038 void 1039 isc__timermgr_dispatch(isc_timermgr_t *manager0) { 1040 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 1041 isc_time_t now; 1042 1043 #ifdef USE_SHARED_MANAGER 1044 if (manager == NULL) 1045 manager = timermgr; 1046 #endif 1047 if (manager == NULL) 1048 return; 1049 TIME_NOW(&now); 1050 dispatch(manager, &now); 1051 } 1052 #endif /* USE_TIMER_THREAD */ 1053 1054 isc_result_t 1055 isc__timer_register(void) { 1056 return (isc_timer_register(isc__timermgr_create)); 1057 } 1058 1059 static isc_mutex_t createlock; 1060 static isc_once_t once = ISC_ONCE_INIT; 1061 static isc_timermgrcreatefunc_t timermgr_createfunc = NULL; 1062 1063 static void 1064 initialize(void) { 1065 RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); 1066 } 1067 1068 isc_result_t 1069 isc_timer_register(isc_timermgrcreatefunc_t createfunc) { 1070 isc_result_t result = ISC_R_SUCCESS; 1071 1072 RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); 1073 1074 LOCK(&createlock); 1075 if (timermgr_createfunc == NULL) 1076 timermgr_createfunc = createfunc; 1077 else 1078 result = ISC_R_EXISTS; 1079 UNLOCK(&createlock); 1080 1081 return (result); 1082 } 1083 1084 isc_result_t 1085 isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, 1086 isc_timermgr_t **managerp) 1087 { 1088 isc_result_t result; 1089 1090 LOCK(&createlock); 1091 1092 REQUIRE(timermgr_createfunc != NULL); 1093 result = (*timermgr_createfunc)(mctx, managerp); 1094 1095 UNLOCK(&createlock); 1096 1097 if (result == ISC_R_SUCCESS) 1098 isc_appctx_settimermgr(actx, *managerp); 1099 1100 return (result); 1101 } 1102 1103 isc_result_t 1104 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { 1105 isc_result_t result; 1106 1107 if (isc_bind9) 1108 return (isc__timermgr_create(mctx, managerp)); 1109 1110 LOCK(&createlock); 1111 1112 REQUIRE(timermgr_createfunc != NULL); 1113 result = (*timermgr_createfunc)(mctx, managerp); 1114 1115 UNLOCK(&createlock); 1116 1117 return (result); 1118 } 1119 1120 void 1121 isc_timermgr_destroy(isc_timermgr_t **managerp) { 1122 REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp)); 1123 1124 if (isc_bind9) 1125 isc__timermgr_destroy(managerp); 1126 else 1127 (*managerp)->methods->destroy(managerp); 1128 1129 ENSURE(*managerp == NULL); 1130 } 1131 1132 isc_result_t 1133 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type, 1134 const isc_time_t *expires, const isc_interval_t *interval, 1135 isc_task_t *task, isc_taskaction_t action, void *arg, 1136 isc_timer_t **timerp) 1137 { 1138 REQUIRE(ISCAPI_TIMERMGR_VALID(manager)); 1139 1140 if (isc_bind9) 1141 return (isc__timer_create(manager, type, expires, interval, 1142 task, action, arg, timerp)); 1143 1144 return (manager->methods->timercreate(manager, type, expires, 1145 interval, task, action, arg, 1146 timerp)); 1147 } 1148 1149 void 1150 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) { 1151 REQUIRE(ISCAPI_TIMER_VALID(timer)); 1152 REQUIRE(timerp != NULL && *timerp == NULL); 1153 1154 if (isc_bind9) 1155 isc__timer_attach(timer, timerp); 1156 else 1157 timer->methods->attach(timer, timerp); 1158 1159 ENSURE(*timerp == timer); 1160 } 1161 1162 void 1163 isc_timer_detach(isc_timer_t **timerp) { 1164 REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp)); 1165 1166 if (isc_bind9) 1167 isc__timer_detach(timerp); 1168 else 1169 (*timerp)->methods->detach(timerp); 1170 1171 ENSURE(*timerp == NULL); 1172 } 1173 1174 isc_result_t 1175 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type, 1176 const isc_time_t *expires, const isc_interval_t *interval, 1177 isc_boolean_t purge) 1178 { 1179 REQUIRE(ISCAPI_TIMER_VALID(timer)); 1180 1181 if (isc_bind9) 1182 return (isc__timer_reset(timer, type, expires, 1183 interval, purge)); 1184 1185 return (timer->methods->reset(timer, type, expires, interval, purge)); 1186 } 1187 1188 isc_result_t 1189 isc_timer_touch(isc_timer_t *timer) { 1190 REQUIRE(ISCAPI_TIMER_VALID(timer)); 1191 1192 if (isc_bind9) 1193 return (isc__timer_touch(timer)); 1194 1195 return (timer->methods->touch(timer)); 1196 } 1197