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