2 * Copyright (C) 2004, 2005 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.11 2005/10/27 00:27:29 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 isc_time_add(&manager->due, &fifteen, &then);
217 if (isc_time_compare(&then, now) < 0) {
218 SIGNAL(&manager->wakeup);
219 signal_ok = ISC_FALSE;
220 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
221 ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
222 "*** POKED TIMER ***");
226 if (timer->index == 1 && signal_ok) {
227 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
229 "signal (schedule)"));
230 SIGNAL(&manager->wakeup);
232 #else /* ISC_PLATFORM_USETHREADS */
233 if (timer->index == 1 &&
234 isc_time_compare(&timer->due, &manager->due) < 0)
235 manager->due = timer->due;
236 #endif /* ISC_PLATFORM_USETHREADS */
238 return (ISC_R_SUCCESS);
242 deschedule(isc_timer_t *timer) {
243 isc_boolean_t need_wakeup = ISC_FALSE;
244 isc_timermgr_t *manager;
247 * The caller must ensure locking.
250 manager = timer->manager;
251 if (timer->index > 0) {
252 if (timer->index == 1)
253 need_wakeup = ISC_TRUE;
254 isc_heap_delete(manager->heap, timer->index);
256 INSIST(manager->nscheduled > 0);
257 manager->nscheduled--;
258 #ifdef ISC_PLATFORM_USETHREADS
260 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
261 ISC_MSG_SIGNALDESCHED,
262 "signal (deschedule)"));
263 SIGNAL(&manager->wakeup);
265 #endif /* ISC_PLATFORM_USETHREADS */
270 destroy(isc_timer_t *timer) {
271 isc_timermgr_t *manager = timer->manager;
274 * The caller must ensure it is safe to destroy the timer.
277 LOCK(&manager->lock);
279 (void)isc_task_purgerange(timer->task,
281 ISC_TIMEREVENT_FIRSTEVENT,
282 ISC_TIMEREVENT_LASTEVENT,
285 UNLINK(manager->timers, timer, link);
287 UNLOCK(&manager->lock);
289 isc_task_detach(&timer->task);
290 DESTROYLOCK(&timer->lock);
292 isc_mem_put(manager->mctx, timer, sizeof(*timer));
296 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
297 isc_time_t *expires, isc_interval_t *interval,
298 isc_task_t *task, isc_taskaction_t action, const void *arg,
299 isc_timer_t **timerp)
306 * Create a new 'type' timer managed by 'manager'. The timers
307 * parameters are specified by 'expires' and 'interval'. Events
308 * will be posted to 'task' and when dispatched 'action' will be
309 * called with 'arg' as the arg value. The new timer is returned
313 REQUIRE(VALID_MANAGER(manager));
314 REQUIRE(task != NULL);
315 REQUIRE(action != NULL);
317 expires = isc_time_epoch;
318 if (interval == NULL)
319 interval = isc_interval_zero;
320 REQUIRE(type == isc_timertype_inactive ||
321 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
322 REQUIRE(timerp != NULL && *timerp == NULL);
323 REQUIRE(type != isc_timertype_limited ||
324 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
329 if (type != isc_timertype_inactive) {
333 * We don't have to do this, but it keeps the compiler from
334 * complaining about "now" possibly being used without being
335 * set, even though it will never actually happen.
337 isc_time_settoepoch(&now);
341 timer = isc_mem_get(manager->mctx, sizeof(*timer));
343 return (ISC_R_NOMEMORY);
345 timer->manager = manager;
346 timer->references = 1;
348 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
349 result = isc_time_add(&now, interval, &timer->idle);
350 if (result != ISC_R_SUCCESS)
353 isc_time_settoepoch(&timer->idle);
356 timer->expires = *expires;
357 timer->interval = *interval;
359 isc_task_attach(task, &timer->task);
360 timer->action = action;
362 * Removing the const attribute from "arg" is the best of two
363 * evils here. If the timer->arg member is made const, then
364 * it affects a great many recipients of the timer event
365 * which did not pass in an "arg" that was truly const.
366 * Changing isc_timer_create() to not have "arg" prototyped as const,
367 * though, can cause compilers warnings for calls that *do*
368 * have a truly const arg. The caller will have to carefully
369 * keep track of whether arg started as a true const.
371 DE_CONST(arg, timer->arg);
373 if (isc_mutex_init(&timer->lock) != ISC_R_SUCCESS) {
374 isc_task_detach(&timer->task);
375 isc_mem_put(manager->mctx, timer, sizeof(*timer));
376 UNEXPECTED_ERROR(__FILE__, __LINE__,
377 "isc_mutex_init() %s",
378 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
379 ISC_MSG_FAILED, "failed"));
380 return (ISC_R_UNEXPECTED);
382 ISC_LINK_INIT(timer, link);
383 timer->magic = TIMER_MAGIC;
385 LOCK(&manager->lock);
388 * Note we don't have to lock the timer like we normally would because
389 * there are no external references to it yet.
392 if (type != isc_timertype_inactive)
393 result = schedule(timer, &now, ISC_TRUE);
395 result = ISC_R_SUCCESS;
396 if (result == ISC_R_SUCCESS)
397 APPEND(manager->timers, timer, link);
399 UNLOCK(&manager->lock);
401 if (result != ISC_R_SUCCESS) {
403 DESTROYLOCK(&timer->lock);
404 isc_task_detach(&timer->task);
405 isc_mem_put(manager->mctx, timer, sizeof(*timer));
411 return (ISC_R_SUCCESS);
415 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
416 isc_time_t *expires, isc_interval_t *interval,
420 isc_timermgr_t *manager;
424 * Change the timer's type, expires, and interval values to the given
425 * values. If 'purge' is ISC_TRUE, any pending events from this timer
426 * are purged from its task's event queue.
429 REQUIRE(VALID_TIMER(timer));
430 manager = timer->manager;
431 REQUIRE(VALID_MANAGER(manager));
433 expires = isc_time_epoch;
434 if (interval == NULL)
435 interval = isc_interval_zero;
436 REQUIRE(type == isc_timertype_inactive ||
437 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
438 REQUIRE(type != isc_timertype_limited ||
439 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
444 if (type != isc_timertype_inactive) {
448 * We don't have to do this, but it keeps the compiler from
449 * complaining about "now" possibly being used without being
450 * set, even though it will never actually happen.
452 isc_time_settoepoch(&now);
455 manager = timer->manager;
457 LOCK(&manager->lock);
461 (void)isc_task_purgerange(timer->task,
463 ISC_TIMEREVENT_FIRSTEVENT,
464 ISC_TIMEREVENT_LASTEVENT,
467 timer->expires = *expires;
468 timer->interval = *interval;
469 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
470 result = isc_time_add(&now, interval, &timer->idle);
472 isc_time_settoepoch(&timer->idle);
473 result = ISC_R_SUCCESS;
476 if (result == ISC_R_SUCCESS) {
477 if (type == isc_timertype_inactive) {
479 result = ISC_R_SUCCESS;
481 result = schedule(timer, &now, ISC_TRUE);
484 UNLOCK(&timer->lock);
485 UNLOCK(&manager->lock);
491 isc_timer_gettype(isc_timer_t *timer) {
494 REQUIRE(VALID_TIMER(timer));
498 UNLOCK(&timer->lock);
504 isc_timer_touch(isc_timer_t *timer) {
509 * Set the last-touched time of 'timer' to the current time.
512 REQUIRE(VALID_TIMER(timer));
519 * REQUIRE(timer->type == isc_timertype_once);
521 * but we cannot without locking the manager lock too, which we
526 result = isc_time_add(&now, &timer->interval, &timer->idle);
528 UNLOCK(&timer->lock);
534 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
536 * Attach *timerp to timer.
539 REQUIRE(VALID_TIMER(timer));
540 REQUIRE(timerp != NULL && *timerp == NULL);
544 UNLOCK(&timer->lock);
550 isc_timer_detach(isc_timer_t **timerp) {
552 isc_boolean_t free_timer = ISC_FALSE;
555 * Detach *timerp from its timer.
558 REQUIRE(timerp != NULL);
560 REQUIRE(VALID_TIMER(timer));
563 REQUIRE(timer->references > 0);
565 if (timer->references == 0)
566 free_timer = ISC_TRUE;
567 UNLOCK(&timer->lock);
576 dispatch(isc_timermgr_t *manager, isc_time_t *now) {
577 isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
579 isc_eventtype_t type = 0;
584 * The caller must be holding the manager lock.
587 while (manager->nscheduled > 0 && !done) {
588 timer = isc_heap_element(manager->heap, 1);
589 INSIST(timer->type != isc_timertype_inactive);
590 if (isc_time_compare(now, &timer->due) >= 0) {
591 if (timer->type == isc_timertype_ticker) {
592 type = ISC_TIMEREVENT_TICK;
593 post_event = ISC_TRUE;
594 need_schedule = ISC_TRUE;
595 } else if (timer->type == isc_timertype_limited) {
597 cmp = isc_time_compare(now, &timer->expires);
599 type = ISC_TIMEREVENT_LIFE;
600 post_event = ISC_TRUE;
601 need_schedule = ISC_FALSE;
603 type = ISC_TIMEREVENT_TICK;
604 post_event = ISC_TRUE;
605 need_schedule = ISC_TRUE;
607 } else if (!isc_time_isepoch(&timer->expires) &&
608 isc_time_compare(now,
609 &timer->expires) >= 0) {
610 type = ISC_TIMEREVENT_LIFE;
611 post_event = ISC_TRUE;
612 need_schedule = ISC_FALSE;
613 } else if (!isc_time_isepoch(&timer->idle) &&
614 isc_time_compare(now,
615 &timer->idle) >= 0) {
616 type = ISC_TIMEREVENT_IDLE;
617 post_event = ISC_TRUE;
618 need_schedule = ISC_FALSE;
621 * Idle timer has been touched; reschedule.
623 XTRACEID(isc_msgcat_get(isc_msgcat,
628 post_event = ISC_FALSE;
629 need_schedule = ISC_TRUE;
633 XTRACEID(isc_msgcat_get(isc_msgcat,
638 * XXX We could preallocate this event.
640 event = isc_event_allocate(manager->mctx,
648 isc_task_send(timer->task, &event);
650 UNEXPECTED_ERROR(__FILE__, __LINE__,
651 isc_msgcat_get(isc_msgcat,
653 ISC_MSG_EVENTNOTALLOC,
659 isc_heap_delete(manager->heap, 1);
660 manager->nscheduled--;
663 result = schedule(timer, now, ISC_FALSE);
664 if (result != ISC_R_SUCCESS)
665 UNEXPECTED_ERROR(__FILE__, __LINE__,
666 isc_msgcat_get(isc_msgcat,
670 "schedule timer: %u"),
674 manager->due = timer->due;
680 #ifdef ISC_PLATFORM_USETHREADS
681 static isc_threadresult_t
682 #ifdef _WIN32 /* XXXDCL */
686 isc_timermgr_t *manager = uap;
690 LOCK(&manager->lock);
691 while (!manager->done) {
694 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
698 dispatch(manager, &now);
700 if (manager->nscheduled > 0) {
701 XTRACETIME2(isc_msgcat_get(isc_msgcat,
706 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
707 INSIST(result == ISC_R_SUCCESS ||
708 result == ISC_R_TIMEDOUT);
710 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
711 ISC_MSG_WAIT, "wait"), now);
712 WAIT(&manager->wakeup, &manager->lock);
714 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
715 ISC_MSG_WAKEUP, "wakeup"));
717 UNLOCK(&manager->lock);
719 return ((isc_threadresult_t)0);
721 #endif /* ISC_PLATFORM_USETHREADS */
724 sooner(void *v1, void *v2) {
725 isc_timer_t *t1, *t2;
729 REQUIRE(VALID_TIMER(t1));
730 REQUIRE(VALID_TIMER(t2));
732 if (isc_time_compare(&t1->due, &t2->due) < 0)
738 set_index(void *what, unsigned int index) {
742 REQUIRE(VALID_TIMER(timer));
744 timer->index = index;
748 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
749 isc_timermgr_t *manager;
753 * Create a timer manager.
756 REQUIRE(managerp != NULL && *managerp == NULL);
758 #ifndef ISC_PLATFORM_USETHREADS
759 if (timermgr != NULL) {
761 *managerp = timermgr;
762 return (ISC_R_SUCCESS);
764 #endif /* ISC_PLATFORM_USETHREADS */
766 manager = isc_mem_get(mctx, sizeof(*manager));
768 return (ISC_R_NOMEMORY);
770 manager->magic = TIMER_MANAGER_MAGIC;
771 manager->mctx = NULL;
772 manager->done = ISC_FALSE;
773 INIT_LIST(manager->timers);
774 manager->nscheduled = 0;
775 isc_time_settoepoch(&manager->due);
776 manager->heap = NULL;
777 result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
778 if (result != ISC_R_SUCCESS) {
779 INSIST(result == ISC_R_NOMEMORY);
780 isc_mem_put(mctx, manager, sizeof(*manager));
781 return (ISC_R_NOMEMORY);
783 if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) {
784 isc_heap_destroy(&manager->heap);
785 isc_mem_put(mctx, manager, sizeof(*manager));
786 UNEXPECTED_ERROR(__FILE__, __LINE__,
787 "isc_mutex_init() %s",
788 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
789 ISC_MSG_FAILED, "failed"));
790 return (ISC_R_UNEXPECTED);
792 isc_mem_attach(mctx, &manager->mctx);
793 #ifdef ISC_PLATFORM_USETHREADS
794 if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
795 isc_mem_detach(&manager->mctx);
796 DESTROYLOCK(&manager->lock);
797 isc_heap_destroy(&manager->heap);
798 isc_mem_put(mctx, manager, sizeof(*manager));
799 UNEXPECTED_ERROR(__FILE__, __LINE__,
800 "isc_condition_init() %s",
801 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
802 ISC_MSG_FAILED, "failed"));
803 return (ISC_R_UNEXPECTED);
805 if (isc_thread_create(run, manager, &manager->thread) !=
807 isc_mem_detach(&manager->mctx);
808 (void)isc_condition_destroy(&manager->wakeup);
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_thread_create() %s",
814 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
815 ISC_MSG_FAILED, "failed"));
816 return (ISC_R_UNEXPECTED);
818 #else /* ISC_PLATFORM_USETHREADS */
821 #endif /* ISC_PLATFORM_USETHREADS */
825 return (ISC_R_SUCCESS);
829 isc_timermgr_poke(isc_timermgr_t *manager) {
830 #ifdef ISC_PLATFORM_USETHREADS
831 REQUIRE(VALID_MANAGER(manager));
833 SIGNAL(&manager->wakeup);
840 isc_timermgr_destroy(isc_timermgr_t **managerp) {
841 isc_timermgr_t *manager;
845 * Destroy a timer manager.
848 REQUIRE(managerp != NULL);
850 REQUIRE(VALID_MANAGER(manager));
852 LOCK(&manager->lock);
854 #ifndef ISC_PLATFORM_USETHREADS
855 if (manager->refs > 1) {
857 UNLOCK(&manager->lock);
862 isc__timermgr_dispatch();
863 #endif /* ISC_PLATFORM_USETHREADS */
865 REQUIRE(EMPTY(manager->timers));
866 manager->done = ISC_TRUE;
868 #ifdef ISC_PLATFORM_USETHREADS
869 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
870 ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
871 SIGNAL(&manager->wakeup);
872 #endif /* ISC_PLATFORM_USETHREADS */
874 UNLOCK(&manager->lock);
876 #ifdef ISC_PLATFORM_USETHREADS
878 * Wait for thread to exit.
880 if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
881 UNEXPECTED_ERROR(__FILE__, __LINE__,
882 "isc_thread_join() %s",
883 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
884 ISC_MSG_FAILED, "failed"));
885 #endif /* ISC_PLATFORM_USETHREADS */
890 #ifdef ISC_PLATFORM_USETHREADS
891 (void)isc_condition_destroy(&manager->wakeup);
892 #endif /* ISC_PLATFORM_USETHREADS */
893 DESTROYLOCK(&manager->lock);
894 isc_heap_destroy(&manager->heap);
896 mctx = manager->mctx;
897 isc_mem_put(mctx, manager, sizeof(*manager));
898 isc_mem_detach(&mctx);
903 #ifndef ISC_PLATFORM_USETHREADS
905 isc__timermgr_nextevent(isc_time_t *when) {
906 if (timermgr == NULL || timermgr->nscheduled == 0)
907 return (ISC_R_NOTFOUND);
908 *when = timermgr->due;
909 return (ISC_R_SUCCESS);
913 isc__timermgr_dispatch(void) {
915 if (timermgr == NULL)
918 dispatch(timermgr, &now);
920 #endif /* ISC_PLATFORM_USETHREADS */