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