timer.c revision 258945
174462Salfred/* 274462Salfred * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC") 3258578Shrs * Copyright (C) 1998-2002 Internet Software Consortium. 4258578Shrs * 5258578Shrs * Permission to use, copy, modify, and/or distribute this software for any 68858Srgrimes * purpose with or without fee is hereby granted, provided that the above 7258578Shrs * copyright notice and this permission notice appear in all copies. 8258578Shrs * 9258578Shrs * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10258578Shrs * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11258578Shrs * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12258578Shrs * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13258578Shrs * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14258578Shrs * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15258578Shrs * PERFORMANCE OF THIS SOFTWARE. 16258578Shrs */ 178858Srgrimes 18258578Shrs/* $Id: timer.c,v 1.84.58.4 2009/01/23 23:47:21 tbox Exp $ */ 19258578Shrs 20258578Shrs/*! \file */ 21258578Shrs 22258578Shrs#include <config.h> 23258578Shrs 24258578Shrs#include <isc/condition.h> 25258578Shrs#include <isc/heap.h> 26258578Shrs#include <isc/log.h> 27258578Shrs#include <isc/magic.h> 28258578Shrs#include <isc/mem.h> 298858Srgrimes#include <isc/msgs.h> 301903Swollman#include <isc/platform.h> 311903Swollman#include <isc/task.h> 3250473Speter#include <isc/thread.h> 331839Swollman#include <isc/time.h> 341839Swollman#include <isc/timer.h> 351839Swollman#include <isc/util.h> 361839Swollman 371839Swollman#ifndef ISC_PLATFORM_USETHREADS 381839Swollman#include "timer_p.h" 391839Swollman#endif /* ISC_PLATFORM_USETHREADS */ 401839Swollman 411839Swollman#ifdef ISC_TIMER_TRACE 4274462Salfred#define XTRACE(s) fprintf(stderr, "%s\n", (s)) 4374462Salfred#define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t)) 441903Swollman#define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \ 4574462Salfred (d).seconds, (d).nanoseconds) 461839Swollman#define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \ 471839Swollman (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds) 481839Swollman#define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \ 491839Swollman (d).seconds, (d).nanoseconds) 501839Swollman#else 511839Swollman#define XTRACE(s) 521839Swollman#define XTRACEID(s, t) 531839Swollman#define XTRACETIME(s, d) 541839Swollman#define XTRACETIME2(s, d, n) 551839Swollman#define XTRACETIMER(s, t, d) 561839Swollman#endif /* ISC_TIMER_TRACE */ 571839Swollman 581839Swollman#define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R') 591839Swollman#define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC) 601839Swollman 611839Swollmanstruct isc_timer { 621839Swollman /*! Not locked. */ 631839Swollman unsigned int magic; 641839Swollman isc_timermgr_t * manager; 651839Swollman isc_mutex_t lock; 661839Swollman /*! Locked by timer lock. */ 671839Swollman unsigned int references; 681839Swollman isc_time_t idle; 691839Swollman /*! Locked by manager lock. */ 701839Swollman isc_timertype_t type; 711839Swollman isc_time_t expires; 721839Swollman isc_interval_t interval; 731839Swollman isc_task_t * task; 741839Swollman isc_taskaction_t action; 751839Swollman void * arg; 761839Swollman unsigned int index; 771839Swollman isc_time_t due; 781839Swollman LINK(isc_timer_t) link; 791839Swollman}; 801839Swollman 811839Swollman#define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M') 821839Swollman#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC) 831839Swollman 841839Swollmanstruct isc_timermgr { 851839Swollman /* Not locked. */ 861839Swollman unsigned int magic; 871839Swollman isc_mem_t * mctx; 881839Swollman isc_mutex_t lock; 891839Swollman /* Locked by manager lock. */ 901839Swollman isc_boolean_t done; 911839Swollman LIST(isc_timer_t) timers; 9274462Salfred unsigned int nscheduled; 9374462Salfred isc_time_t due; 941839Swollman#ifdef ISC_PLATFORM_USETHREADS 951839Swollman isc_condition_t wakeup; 961839Swollman isc_thread_t thread; 971839Swollman#else /* ISC_PLATFORM_USETHREADS */ 981839Swollman unsigned int refs; 991839Swollman#endif /* ISC_PLATFORM_USETHREADS */ 1001839Swollman isc_heap_t * heap; 1011839Swollman}; 1021839Swollman 1031839Swollman#ifndef ISC_PLATFORM_USETHREADS 1041839Swollman/*! 1051839Swollman * If threads are not in use, there can be only one. 1061839Swollman */ 1071839Swollmanstatic isc_timermgr_t *timermgr = NULL; 1081839Swollman#endif /* ISC_PLATFORM_USETHREADS */ 1091839Swollman 1101839Swollmanstatic inline isc_result_t 1111839Swollmanschedule(isc_timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) { 11274462Salfred isc_result_t result; 11374462Salfred isc_timermgr_t *manager; 1141839Swollman isc_time_t due; 1151839Swollman int cmp; 1161839Swollman#ifdef ISC_PLATFORM_USETHREADS 1171839Swollman isc_boolean_t timedwait; 1181839Swollman#endif 1191839Swollman 1201839Swollman /*! 1211839Swollman * Note: the caller must ensure locking. 1221839Swollman */ 1231839Swollman 1241839Swollman REQUIRE(timer->type != isc_timertype_inactive); 1251839Swollman 1261839Swollman#ifndef ISC_PLATFORM_USETHREADS 1271839Swollman UNUSED(signal_ok); 1281839Swollman#endif /* ISC_PLATFORM_USETHREADS */ 1291839Swollman 1301839Swollman manager = timer->manager; 1311839Swollman 1321839Swollman#ifdef ISC_PLATFORM_USETHREADS 1331839Swollman /*! 1341839Swollman * If the manager was timed wait, we may need to signal the 1351839Swollman * manager to force a wakeup. 1361839Swollman */ 1371839Swollman timedwait = ISC_TF(manager->nscheduled > 0 && 13874462Salfred isc_time_seconds(&manager->due) != 0); 13974462Salfred#endif 14074462Salfred 14174462Salfred /* 1421839Swollman * Compute the new due time. 1431839Swollman */ 1441839Swollman if (timer->type != isc_timertype_once) { 1451839Swollman result = isc_time_add(now, &timer->interval, &due); 1461839Swollman if (result != ISC_R_SUCCESS) 1471839Swollman return (result); 1481839Swollman if (timer->type == isc_timertype_limited && 1491839Swollman isc_time_compare(&timer->expires, &due) < 0) 15021059Speter due = timer->expires; 1511839Swollman } else { 1521839Swollman if (isc_time_isepoch(&timer->idle)) 1531839Swollman due = timer->expires; 1541839Swollman else if (isc_time_isepoch(&timer->expires)) 1551839Swollman due = timer->idle; 1561839Swollman else if (isc_time_compare(&timer->idle, &timer->expires) < 0) 1571839Swollman due = timer->idle; 1581839Swollman else 1591839Swollman due = timer->expires; 1601839Swollman } 1611839Swollman 1621903Swollman /* 1631839Swollman * Schedule the timer. 1641839Swollman */ 1651839Swollman 1661839Swollman if (timer->index > 0) { 1671839Swollman /* 1681839Swollman * Already scheduled. 16993032Simp */ 1701839Swollman cmp = isc_time_compare(&due, &timer->due); 1711839Swollman timer->due = due; 1721839Swollman switch (cmp) { 1731839Swollman case -1: 1741839Swollman isc_heap_increased(manager->heap, timer->index); 1751839Swollman break; 1761839Swollman case 1: 17793032Simp isc_heap_decreased(manager->heap, timer->index); 1781839Swollman break; 1791839Swollman case 0: 1801839Swollman /* Nothing to do. */ 1811839Swollman break; 1821839Swollman } 1831839Swollman } else { 1841839Swollman timer->due = due; 18593032Simp result = isc_heap_insert(manager->heap, timer); 1861839Swollman if (result != ISC_R_SUCCESS) { 18774462Salfred INSIST(result == ISC_R_NOMEMORY); 1881839Swollman return (ISC_R_NOMEMORY); 189108533Sschweikh } 19074462Salfred manager->nscheduled++; 19174462Salfred } 19274462Salfred 19374462Salfred XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, 19493032Simp ISC_MSG_SCHEDULE, "schedule"), timer, due); 19574462Salfred 19674462Salfred /* 19774462Salfred * If this timer is at the head of the queue, we need to ensure 19874462Salfred * that we won't miss it if it has a more recent due time than 19974462Salfred * the current "next" timer. We do this either by waking up the 20074462Salfred * run thread, or explicitly setting the value in the manager. 20174462Salfred */ 20293032Simp#ifdef ISC_PLATFORM_USETHREADS 20374462Salfred 20474462Salfred /* 2051839Swollman * This is a temporary (probably) hack to fix a bug on tru64 5.1 2061839Swollman * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually 2071839Swollman * return when the time expires, so here, we check to see if 2081839Swollman * we're 15 seconds or more behind, and if we are, we signal 2091839Swollman * the dispatcher. This isn't such a bad idea as a general purpose 21093032Simp * watchdog, so perhaps we should just leave it in here. 2111903Swollman */ 2121903Swollman if (signal_ok && timedwait) { 21374462Salfred 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