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