Return to timer.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / lib / isc |
1.1 ! misho 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: ! 61: struct 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: ! 84: struct 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: */ ! 107: static isc_timermgr_t *timermgr = NULL; ! 108: #endif /* ISC_PLATFORM_USETHREADS */ ! 109: ! 110: static inline isc_result_t ! 111: schedule(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: ! 244: static inline void ! 245: deschedule(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: ! 272: static void ! 273: destroy(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: ! 298: isc_result_t ! 299: isc_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: ! 416: isc_result_t ! 417: isc_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: ! 492: isc_timertype_t ! 493: isc_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: ! 505: isc_result_t ! 506: isc_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: ! 535: void ! 536: isc_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: ! 551: void ! 552: isc_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: ! 577: static void ! 578: dispatch(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 ! 697: static isc_threadresult_t ! 698: #ifdef _WIN32 /* XXXDCL */ ! 699: WINAPI ! 700: #endif ! 701: run(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: ! 739: static isc_boolean_t ! 740: sooner(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: ! 753: static void ! 754: set_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: ! 763: isc_result_t ! 764: isc_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: ! 841: void ! 842: isc_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: ! 852: void ! 853: isc_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 ! 917: isc_result_t ! 918: isc__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: ! 925: void ! 926: isc__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 */