Annotation of embedaddon/ntp/lib/isc/timer.c, revision 1.1
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>