2 * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.
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.
18 /* $Id: timer.c,v 1.64.12.13 2006/01/04 23:50:21 marka Exp $ */
22 #include <isc/condition.h>
25 #include <isc/magic.h>
28 #include <isc/platform.h>
30 #include <isc/thread.h>
32 #include <isc/timer.h>
35 #ifndef ISC_PLATFORM_USETHREADS
37 #endif /* ISC_PLATFORM_USETHREADS */
39 #ifdef ISC_TIMER_TRACE
40 #define XTRACE(s) fprintf(stderr, "%s\n", (s))
41 #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
42 #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \
43 (d).seconds, (d).nanoseconds)
44 #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
45 (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
46 #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
47 (d).seconds, (d).nanoseconds)
50 #define XTRACEID(s, t)
51 #define XTRACETIME(s, d)
52 #define XTRACETIME2(s, d, n)
53 #define XTRACETIMER(s, t, d)
54 #endif /* ISC_TIMER_TRACE */
56 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
57 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
62 isc_timermgr_t * manager;
64 /* Locked by timer lock. */
65 unsigned int references;
67 /* Locked by manager lock. */
70 isc_interval_t interval;
72 isc_taskaction_t action;
76 LINK(isc_timer_t) link;
79 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
80 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
87 /* Locked by manager lock. */
89 LIST(isc_timer_t) timers;
90 unsigned int nscheduled;
92 #ifdef ISC_PLATFORM_USETHREADS
93 isc_condition_t wakeup;
95 #else /* ISC_PLATFORM_USETHREADS */
97 #endif /* ISC_PLATFORM_USETHREADS */
101 #ifndef ISC_PLATFORM_USETHREADS
103 * If threads are not in use, there can be only one.
105 static isc_timermgr_t *timermgr = NULL;
106 #endif /* ISC_PLATFORM_USETHREADS */
108 static inline isc_result_t
109 schedule(isc_timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
111 isc_timermgr_t *manager;
114 #ifdef ISC_PLATFORM_USETHREADS
115 isc_boolean_t timedwait;
119 * Note: the caller must ensure locking.
122 REQUIRE(timer->type != isc_timertype_inactive);
124 #ifndef ISC_PLATFORM_USETHREADS
126 #endif /* ISC_PLATFORM_USETHREADS */
128 manager = timer->manager;
130 #ifdef ISC_PLATFORM_USETHREADS
132 * If the manager was timed wait, we may need to signal the
133 * manager to force a wakeup.
135 timedwait = ISC_TF(manager->nscheduled > 0 &&
136 isc_time_seconds(&manager->due) != 0);
140 * Compute the new due time.
142 if (timer->type != isc_timertype_once) {
143 result = isc_time_add(now, &timer->interval, &due);
144 if (result != ISC_R_SUCCESS)
146 if (timer->type == isc_timertype_limited &&
147 isc_time_compare(&timer->expires, &due) < 0)
148 due = timer->expires;
150 if (isc_time_isepoch(&timer->idle))
151 due = timer->expires;
152 else if (isc_time_isepoch(&timer->expires))
154 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
157 due = timer->expires;
161 * Schedule the timer.
164 if (timer->index > 0) {
168 cmp = isc_time_compare(&due, &timer->due);
172 isc_heap_increased(manager->heap, timer->index);
175 isc_heap_decreased(manager->heap, timer->index);
183 result = isc_heap_insert(manager->heap, timer);
184 if (result != ISC_R_SUCCESS) {
185 INSIST(result == ISC_R_NOMEMORY);
186 return (ISC_R_NOMEMORY);
188 manager->nscheduled++;
191 XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
192 ISC_MSG_SCHEDULE, "schedule"), timer, due);
195 * If this timer is at the head of the queue, we need to ensure
196 * that we won't miss it if it has a more recent due time than
197 * the current "next" timer. We do this either by waking up the
198 * run thread, or explicitly setting the value in the manager.
200 #ifdef ISC_PLATFORM_USETHREADS
203 * This is a temporary (probably) hack to fix a bug on tru64 5.1
204 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
205 * return when the time expires, so here, we check to see if
206 * we're 15 seconds or more behind, and if we are, we signal
207 * the dispatcher. This isn't such a bad idea as a general purpose
208 * watchdog, so perhaps we should just leave it in here.
210 if (signal_ok && timedwait) {
211 isc_interval_t fifteen;
214 isc_interval_set(&fifteen, 15, 0);
215 result = isc_time_add(&manager->due, &fifteen, &then);
217 if (result == ISC_R_SUCCESS &&
218 isc_time_compare(&then, now) < 0) {
219 SIGNAL(&manager->wakeup);
220 signal_ok = ISC_FALSE;
221 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
222 ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
223 "*** POKED TIMER ***");
227 if (timer->index == 1 && signal_ok) {
228 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
230 "signal (schedule)"));
231 SIGNAL(&manager->wakeup);
233 #else /* ISC_PLATFORM_USETHREADS */
234 if (timer->index == 1 &&
235 isc_time_compare(&timer->due, &manager->due) < 0)
236 manager->due = timer->due;
237 #endif /* ISC_PLATFORM_USETHREADS */
239 return (ISC_R_SUCCESS);
243 deschedule(isc_timer_t *timer) {
244 isc_boolean_t need_wakeup = ISC_FALSE;
245 isc_timermgr_t *manager;
248 * The caller must ensure locking.
251 manager = timer->manager;
252 if (timer->index > 0) {
253 if (timer->index == 1)
254 need_wakeup = ISC_TRUE;
255 isc_heap_delete(manager->heap, timer->index);
257 INSIST(manager->nscheduled > 0);
258 manager->nscheduled--;
259 #ifdef ISC_PLATFORM_USETHREADS
261 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
262 ISC_MSG_SIGNALDESCHED,
263 "signal (deschedule)"));
264 SIGNAL(&manager->wakeup);
266 #endif /* ISC_PLATFORM_USETHREADS */
271 destroy(isc_timer_t *timer) {
272 isc_timermgr_t *manager = timer->manager;
275 * The caller must ensure it is safe to destroy the timer.
278 LOCK(&manager->lock);
280 (void)isc_task_purgerange(timer->task,
282 ISC_TIMEREVENT_FIRSTEVENT,
283 ISC_TIMEREVENT_LASTEVENT,
286 UNLINK(manager->timers, timer, link);
288 UNLOCK(&manager->lock);
290 isc_task_detach(&timer->task);
291 DESTROYLOCK(&timer->lock);
293 isc_mem_put(manager->mctx, timer, sizeof(*timer));
297 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
298 isc_time_t *expires, isc_interval_t *interval,
299 isc_task_t *task, isc_taskaction_t action, const void *arg,
300 isc_timer_t **timerp)
307 * Create a new 'type' timer managed by 'manager'. The timers
308 * parameters are specified by 'expires' and 'interval'. Events
309 * will be posted to 'task' and when dispatched 'action' will be
310 * called with 'arg' as the arg value. The new timer is returned
314 REQUIRE(VALID_MANAGER(manager));
315 REQUIRE(task != NULL);
316 REQUIRE(action != NULL);
318 expires = isc_time_epoch;
319 if (interval == NULL)
320 interval = isc_interval_zero;
321 REQUIRE(type == isc_timertype_inactive ||
322 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
323 REQUIRE(timerp != NULL && *timerp == NULL);
324 REQUIRE(type != isc_timertype_limited ||
325 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
330 if (type != isc_timertype_inactive) {
334 * We don't have to do this, but it keeps the compiler from
335 * complaining about "now" possibly being used without being
336 * set, even though it will never actually happen.
338 isc_time_settoepoch(&now);
342 timer = isc_mem_get(manager->mctx, sizeof(*timer));
344 return (ISC_R_NOMEMORY);
346 timer->manager = manager;
347 timer->references = 1;
349 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
350 result = isc_time_add(&now, interval, &timer->idle);
351 if (result != ISC_R_SUCCESS) {
352 isc_mem_put(manager->mctx, timer, sizeof(*timer));
356 isc_time_settoepoch(&timer->idle);
359 timer->expires = *expires;
360 timer->interval = *interval;
362 isc_task_attach(task, &timer->task);
363 timer->action = action;
365 * Removing the const attribute from "arg" is the best of two
366 * evils here. If the timer->arg member is made const, then
367 * it affects a great many recipients of the timer event
368 * which did not pass in an "arg" that was truly const.
369 * Changing isc_timer_create() to not have "arg" prototyped as const,
370 * though, can cause compilers warnings for calls that *do*
371 * have a truly const arg. The caller will have to carefully
372 * keep track of whether arg started as a true const.
374 DE_CONST(arg, timer->arg);
376 if (isc_mutex_init(&timer->lock) != ISC_R_SUCCESS) {
377 isc_task_detach(&timer->task);
378 isc_mem_put(manager->mctx, timer, sizeof(*timer));
379 UNEXPECTED_ERROR(__FILE__, __LINE__,
380 "isc_mutex_init() %s",
381 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
382 ISC_MSG_FAILED, "failed"));
383 return (ISC_R_UNEXPECTED);
385 ISC_LINK_INIT(timer, link);
386 timer->magic = TIMER_MAGIC;
388 LOCK(&manager->lock);
391 * Note we don't have to lock the timer like we normally would because
392 * there are no external references to it yet.
395 if (type != isc_timertype_inactive)
396 result = schedule(timer, &now, ISC_TRUE);
398 result = ISC_R_SUCCESS;
399 if (result == ISC_R_SUCCESS)
400 APPEND(manager->timers, timer, link);
402 UNLOCK(&manager->lock);
404 if (result != ISC_R_SUCCESS) {
406 DESTROYLOCK(&timer->lock);
407 isc_task_detach(&timer->task);
408 isc_mem_put(manager->mctx, timer, sizeof(*timer));
414 return (ISC_R_SUCCESS);
418 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
419 isc_time_t *expires, isc_interval_t *interval,
423 isc_timermgr_t *manager;
427 * Change the timer's type, expires, and interval values to the given
428 * values. If 'purge' is ISC_TRUE, any pending events from this timer
429 * are purged from its task's event queue.
432 REQUIRE(VALID_TIMER(timer));
433 manager = timer->manager;
434 REQUIRE(VALID_MANAGER(manager));
436 expires = isc_time_epoch;
437 if (interval == NULL)
438 interval = isc_interval_zero;
439 REQUIRE(type == isc_timertype_inactive ||
440 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
441 REQUIRE(type != isc_timertype_limited ||
442 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
447 if (type != isc_timertype_inactive) {
451 * We don't have to do this, but it keeps the compiler from
452 * complaining about "now" possibly being used without being
453 * set, even though it will never actually happen.
455 isc_time_settoepoch(&now);
458 manager = timer->manager;
460 LOCK(&manager->lock);
464 (void)isc_task_purgerange(timer->task,
466 ISC_TIMEREVENT_FIRSTEVENT,
467 ISC_TIMEREVENT_LASTEVENT,
470 timer->expires = *expires;
471 timer->interval = *interval;
472 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
473 result = isc_time_add(&now, interval, &timer->idle);
475 isc_time_settoepoch(&timer->idle);
476 result = ISC_R_SUCCESS;
479 if (result == ISC_R_SUCCESS) {
480 if (type == isc_timertype_inactive) {
482 result = ISC_R_SUCCESS;
484 result = schedule(timer, &now, ISC_TRUE);
487 UNLOCK(&timer->lock);
488 UNLOCK(&manager->lock);
494 isc_timer_gettype(isc_timer_t *timer) {
497 REQUIRE(VALID_TIMER(timer));
501 UNLOCK(&timer->lock);
507 isc_timer_touch(isc_timer_t *timer) {
512 * Set the last-touched time of 'timer' to the current time.
515 REQUIRE(VALID_TIMER(timer));
522 * REQUIRE(timer->type == isc_timertype_once);
524 * but we cannot without locking the manager lock too, which we
529 result = isc_time_add(&now, &timer->interval, &timer->idle);
531 UNLOCK(&timer->lock);
537 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
539 * Attach *timerp to timer.
542 REQUIRE(VALID_TIMER(timer));
543 REQUIRE(timerp != NULL && *timerp == NULL);
547 UNLOCK(&timer->lock);
553 isc_timer_detach(isc_timer_t **timerp) {
555 isc_boolean_t free_timer = ISC_FALSE;
558 * Detach *timerp from its timer.
561 REQUIRE(timerp != NULL);
563 REQUIRE(VALID_TIMER(timer));
566 REQUIRE(timer->references > 0);
568 if (timer->references == 0)
569 free_timer = ISC_TRUE;
570 UNLOCK(&timer->lock);
579 dispatch(isc_timermgr_t *manager, isc_time_t *now) {
580 isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
582 isc_eventtype_t type = 0;
587 * The caller must be holding the manager lock.
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) {
600 cmp = isc_time_compare(now, &timer->expires);
602 type = ISC_TIMEREVENT_LIFE;
603 post_event = ISC_TRUE;
604 need_schedule = ISC_FALSE;
606 type = ISC_TIMEREVENT_TICK;
607 post_event = ISC_TRUE;
608 need_schedule = ISC_TRUE;
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 if (!isc_time_isepoch(&timer->idle) &&
617 isc_time_compare(now,
618 &timer->idle) >= 0) {
619 type = ISC_TIMEREVENT_IDLE;
620 post_event = ISC_TRUE;
621 need_schedule = ISC_FALSE;
624 * Idle timer has been touched; reschedule.
626 XTRACEID(isc_msgcat_get(isc_msgcat,
631 post_event = ISC_FALSE;
632 need_schedule = ISC_TRUE;
636 XTRACEID(isc_msgcat_get(isc_msgcat,
641 * XXX We could preallocate this event.
643 event = isc_event_allocate(manager->mctx,
651 isc_task_send(timer->task, &event);
653 UNEXPECTED_ERROR(__FILE__, __LINE__,
654 isc_msgcat_get(isc_msgcat,
656 ISC_MSG_EVENTNOTALLOC,
662 isc_heap_delete(manager->heap, 1);
663 manager->nscheduled--;
666 result = schedule(timer, now, ISC_FALSE);
667 if (result != ISC_R_SUCCESS)
668 UNEXPECTED_ERROR(__FILE__, __LINE__,
669 isc_msgcat_get(isc_msgcat,
673 "schedule timer: %u"),
677 manager->due = timer->due;
683 #ifdef ISC_PLATFORM_USETHREADS
684 static isc_threadresult_t
685 #ifdef _WIN32 /* XXXDCL */
689 isc_timermgr_t *manager = uap;
693 LOCK(&manager->lock);
694 while (!manager->done) {
697 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
701 dispatch(manager, &now);
703 if (manager->nscheduled > 0) {
704 XTRACETIME2(isc_msgcat_get(isc_msgcat,
709 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
710 INSIST(result == ISC_R_SUCCESS ||
711 result == ISC_R_TIMEDOUT);
713 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
714 ISC_MSG_WAIT, "wait"), now);
715 WAIT(&manager->wakeup, &manager->lock);
717 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
718 ISC_MSG_WAKEUP, "wakeup"));
720 UNLOCK(&manager->lock);
722 return ((isc_threadresult_t)0);
724 #endif /* ISC_PLATFORM_USETHREADS */
727 sooner(void *v1, void *v2) {
728 isc_timer_t *t1, *t2;
732 REQUIRE(VALID_TIMER(t1));
733 REQUIRE(VALID_TIMER(t2));
735 if (isc_time_compare(&t1->due, &t2->due) < 0)
741 set_index(void *what, unsigned int index) {
745 REQUIRE(VALID_TIMER(timer));
747 timer->index = index;
751 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
752 isc_timermgr_t *manager;
756 * Create a timer manager.
759 REQUIRE(managerp != NULL && *managerp == NULL);
761 #ifndef ISC_PLATFORM_USETHREADS
762 if (timermgr != NULL) {
764 *managerp = timermgr;
765 return (ISC_R_SUCCESS);
767 #endif /* ISC_PLATFORM_USETHREADS */
769 manager = isc_mem_get(mctx, sizeof(*manager));
771 return (ISC_R_NOMEMORY);
773 manager->magic = TIMER_MANAGER_MAGIC;
774 manager->mctx = NULL;
775 manager->done = ISC_FALSE;
776 INIT_LIST(manager->timers);
777 manager->nscheduled = 0;
778 isc_time_settoepoch(&manager->due);
779 manager->heap = NULL;
780 result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
781 if (result != ISC_R_SUCCESS) {
782 INSIST(result == ISC_R_NOMEMORY);
783 isc_mem_put(mctx, manager, sizeof(*manager));
784 return (ISC_R_NOMEMORY);
786 if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
787 isc_heap_destroy(&manager->heap);
788 isc_mem_put(mctx, manager, sizeof(*manager));
789 UNEXPECTED_ERROR(__FILE__, __LINE__,
790 "isc_mutex_init() %s",
791 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
792 ISC_MSG_FAILED, "failed"));
793 return (ISC_R_UNEXPECTED);
795 isc_mem_attach(mctx, &manager->mctx);
796 #ifdef ISC_PLATFORM_USETHREADS
797 if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
798 isc_mem_detach(&manager->mctx);
799 DESTROYLOCK(&manager->lock);
800 isc_heap_destroy(&manager->heap);
801 isc_mem_put(mctx, manager, sizeof(*manager));
802 UNEXPECTED_ERROR(__FILE__, __LINE__,
803 "isc_condition_init() %s",
804 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
805 ISC_MSG_FAILED, "failed"));
806 return (ISC_R_UNEXPECTED);
808 if (isc_thread_create(run, manager, &manager->thread) !=
810 isc_mem_detach(&manager->mctx);
811 (void)isc_condition_destroy(&manager->wakeup);
812 DESTROYLOCK(&manager->lock);
813 isc_heap_destroy(&manager->heap);
814 isc_mem_put(mctx, manager, sizeof(*manager));
815 UNEXPECTED_ERROR(__FILE__, __LINE__,
816 "isc_thread_create() %s",
817 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
818 ISC_MSG_FAILED, "failed"));
819 return (ISC_R_UNEXPECTED);
821 #else /* ISC_PLATFORM_USETHREADS */
824 #endif /* ISC_PLATFORM_USETHREADS */
828 return (ISC_R_SUCCESS);
832 isc_timermgr_poke(isc_timermgr_t *manager) {
833 #ifdef ISC_PLATFORM_USETHREADS
834 REQUIRE(VALID_MANAGER(manager));
836 SIGNAL(&manager->wakeup);
843 isc_timermgr_destroy(isc_timermgr_t **managerp) {
844 isc_timermgr_t *manager;
848 * Destroy a timer manager.
851 REQUIRE(managerp != NULL);
853 REQUIRE(VALID_MANAGER(manager));
855 LOCK(&manager->lock);
857 #ifndef ISC_PLATFORM_USETHREADS
858 if (manager->refs > 1) {
860 UNLOCK(&manager->lock);
865 isc__timermgr_dispatch();
866 #endif /* ISC_PLATFORM_USETHREADS */
868 REQUIRE(EMPTY(manager->timers));
869 manager->done = ISC_TRUE;
871 #ifdef ISC_PLATFORM_USETHREADS
872 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
873 ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
874 SIGNAL(&manager->wakeup);
875 #endif /* ISC_PLATFORM_USETHREADS */
877 UNLOCK(&manager->lock);
879 #ifdef ISC_PLATFORM_USETHREADS
881 * Wait for thread to exit.
883 if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
884 UNEXPECTED_ERROR(__FILE__, __LINE__,
885 "isc_thread_join() %s",
886 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
887 ISC_MSG_FAILED, "failed"));
888 #endif /* ISC_PLATFORM_USETHREADS */
893 #ifdef ISC_PLATFORM_USETHREADS
894 (void)isc_condition_destroy(&manager->wakeup);
895 #endif /* ISC_PLATFORM_USETHREADS */
896 DESTROYLOCK(&manager->lock);
897 isc_heap_destroy(&manager->heap);
899 mctx = manager->mctx;
900 isc_mem_put(mctx, manager, sizeof(*manager));
901 isc_mem_detach(&mctx);
906 #ifndef ISC_PLATFORM_USETHREADS
908 isc__timermgr_nextevent(isc_time_t *when) {
909 if (timermgr == NULL || timermgr->nscheduled == 0)
910 return (ISC_R_NOTFOUND);
911 *when = timermgr->due;
912 return (ISC_R_SUCCESS);
916 isc__timermgr_dispatch(void) {
918 if (timermgr == NULL)
921 dispatch(timermgr, &now);
923 #endif /* ISC_PLATFORM_USETHREADS */