2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2001 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.2.1 2004/03/09 06:11:52 marka Exp $ */
22 #include <isc/condition.h>
24 #include <isc/magic.h>
27 #include <isc/platform.h>
29 #include <isc/thread.h>
31 #include <isc/timer.h>
34 #ifndef ISC_PLATFORM_USETHREADS
36 #endif /* ISC_PLATFORM_USETHREADS */
38 #ifdef ISC_TIMER_TRACE
39 #define XTRACE(s) printf("%s\n", (s))
40 #define XTRACEID(s, t) printf("%s %p\n", (s), (t))
41 #define XTRACETIME(s, d) printf("%s %u.%09u\n", (s), \
42 (d).seconds, (d).nanoseconds)
43 #define XTRACETIMER(s, t, d) printf("%s %p %u.%09u\n", (s), (t), \
44 (d).seconds, (d).nanoseconds)
47 #define XTRACEID(s, t)
48 #define XTRACETIME(s, d)
49 #define XTRACETIMER(s, t, d)
50 #endif /* ISC_TIMER_TRACE */
52 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
53 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
58 isc_timermgr_t
* manager
;
60 /* Locked by timer lock. */
61 unsigned int references
;
63 /* Locked by manager lock. */
66 isc_interval_t interval
;
68 isc_taskaction_t action
;
72 LINK(isc_timer_t
) link
;
75 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
76 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
83 /* Locked by manager lock. */
85 LIST(isc_timer_t
) timers
;
86 unsigned int nscheduled
;
88 #ifdef ISC_PLATFORM_USETHREADS
89 isc_condition_t wakeup
;
91 #else /* ISC_PLATFORM_USETHREADS */
93 #endif /* ISC_PLATFORM_USETHREADS */
97 #ifndef ISC_PLATFORM_USETHREADS
99 * If threads are not in use, there can be only one.
101 static isc_timermgr_t
*timermgr
= NULL
;
102 #endif /* ISC_PLATFORM_USETHREADS */
104 static inline isc_result_t
105 schedule(isc_timer_t
*timer
, isc_time_t
*now
, isc_boolean_t signal_ok
) {
107 isc_timermgr_t
*manager
;
112 * Note: the caller must ensure locking.
115 REQUIRE(timer
->type
!= isc_timertype_inactive
);
117 #ifndef ISC_PLATFORM_USETHREADS
119 #endif /* ISC_PLATFORM_USETHREADS */
122 * Compute the new due time.
124 if (timer
->type
== isc_timertype_ticker
) {
125 result
= isc_time_add(now
, &timer
->interval
, &due
);
126 if (result
!= ISC_R_SUCCESS
)
129 if (isc_time_isepoch(&timer
->idle
))
130 due
= timer
->expires
;
131 else if (isc_time_isepoch(&timer
->expires
))
133 else if (isc_time_compare(&timer
->idle
, &timer
->expires
) < 0)
136 due
= timer
->expires
;
140 * Schedule the timer.
142 manager
= timer
->manager
;
143 if (timer
->index
> 0) {
147 cmp
= isc_time_compare(&due
, &timer
->due
);
151 isc_heap_increased(manager
->heap
, timer
->index
);
154 isc_heap_decreased(manager
->heap
, timer
->index
);
162 result
= isc_heap_insert(manager
->heap
, timer
);
163 if (result
!= ISC_R_SUCCESS
) {
164 INSIST(result
== ISC_R_NOMEMORY
);
165 return (ISC_R_NOMEMORY
);
167 manager
->nscheduled
++;
170 XTRACETIMER(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
171 ISC_MSG_SCHEDULE
, "schedule"), timer
, due
);
174 * If this timer is at the head of the queue, we need to ensure
175 * that we won't miss it if it has a more recent due time than
176 * the current "next" timer. We do this either by waking up the
177 * run thread, or explicitly setting the value in the manager.
179 #ifdef ISC_PLATFORM_USETHREADS
180 if (timer
->index
== 1 && signal_ok
) {
181 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
183 "signal (schedule)"));
184 SIGNAL(&manager
->wakeup
);
186 #else /* ISC_PLATFORM_USETHREADS */
187 if (timer
->index
== 1 &&
188 isc_time_compare(&timer
->due
, &manager
->due
) < 0)
189 manager
->due
= timer
->due
;
190 #endif /* ISC_PLATFORM_USETHREADS */
192 return (ISC_R_SUCCESS
);
196 deschedule(isc_timer_t
*timer
) {
197 isc_boolean_t need_wakeup
= ISC_FALSE
;
198 isc_timermgr_t
*manager
;
201 * The caller must ensure locking.
204 manager
= timer
->manager
;
205 if (timer
->index
> 0) {
206 if (timer
->index
== 1)
207 need_wakeup
= ISC_TRUE
;
208 isc_heap_delete(manager
->heap
, timer
->index
);
210 INSIST(manager
->nscheduled
> 0);
211 manager
->nscheduled
--;
212 #ifdef ISC_PLATFORM_USETHREADS
214 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
215 ISC_MSG_SIGNALDESCHED
,
216 "signal (deschedule)"));
217 SIGNAL(&manager
->wakeup
);
219 #endif /* ISC_PLATFORM_USETHREADS */
224 destroy(isc_timer_t
*timer
) {
225 isc_timermgr_t
*manager
= timer
->manager
;
228 * The caller must ensure it is safe to destroy the timer.
231 LOCK(&manager
->lock
);
233 isc_task_purgerange(timer
->task
,
235 ISC_TIMEREVENT_FIRSTEVENT
,
236 ISC_TIMEREVENT_LASTEVENT
,
239 UNLINK(manager
->timers
, timer
, link
);
241 UNLOCK(&manager
->lock
);
243 isc_task_detach(&timer
->task
);
244 DESTROYLOCK(&timer
->lock
);
246 isc_mem_put(manager
->mctx
, timer
, sizeof *timer
);
250 isc_timer_create(isc_timermgr_t
*manager
, isc_timertype_t type
,
251 isc_time_t
*expires
, isc_interval_t
*interval
,
252 isc_task_t
*task
, isc_taskaction_t action
, const void *arg
,
253 isc_timer_t
**timerp
)
260 * Create a new 'type' timer managed by 'manager'. The timers
261 * parameters are specified by 'expires' and 'interval'. Events
262 * will be posted to 'task' and when dispatched 'action' will be
263 * called with 'arg' as the arg value. The new timer is returned
267 REQUIRE(VALID_MANAGER(manager
));
268 REQUIRE(task
!= NULL
);
269 REQUIRE(action
!= NULL
);
271 expires
= isc_time_epoch
;
272 if (interval
== NULL
)
273 interval
= isc_interval_zero
;
274 REQUIRE(type
== isc_timertype_inactive
||
275 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
276 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
281 if (type
!= isc_timertype_inactive
) {
282 result
= isc_time_now(&now
);
283 if (result
!= ISC_R_SUCCESS
) {
284 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
285 "isc_time_now() %s: %s",
286 isc_msgcat_get(isc_msgcat
,
290 isc_result_totext(result
));
291 return (ISC_R_UNEXPECTED
);
295 * We don't have to do this, but it keeps the compiler from
296 * complaining about "now" possibly being used without being
297 * set, even though it will never actually happen.
299 isc_time_settoepoch(&now
);
303 timer
= isc_mem_get(manager
->mctx
, sizeof *timer
);
305 return (ISC_R_NOMEMORY
);
307 timer
->manager
= manager
;
308 timer
->references
= 1;
310 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
311 result
= isc_time_add(&now
, interval
, &timer
->idle
);
312 if (result
!= ISC_R_SUCCESS
)
315 isc_time_settoepoch(&timer
->idle
);
318 timer
->expires
= *expires
;
319 timer
->interval
= *interval
;
321 isc_task_attach(task
, &timer
->task
);
322 timer
->action
= action
;
324 * Removing the const attribute from "arg" is the best of two
325 * evils here. If the timer->arg member is made const, then
326 * it affects a great many recipients of the timer event
327 * which did not pass in an "arg" that was truly const.
328 * Changing isc_timer_create() to not have "arg" prototyped as const,
329 * though, can cause compilers warnings for calls that *do*
330 * have a truly const arg. The caller will have to carefully
331 * keep track of whether arg started as a true const.
333 DE_CONST(arg
, timer
->arg
);
335 if (isc_mutex_init(&timer
->lock
) != ISC_R_SUCCESS
) {
336 isc_task_detach(&timer
->task
);
337 isc_mem_put(manager
->mctx
, timer
, sizeof *timer
);
338 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
339 "isc_mutex_init() %s",
340 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
341 ISC_MSG_FAILED
, "failed"));
342 return (ISC_R_UNEXPECTED
);
344 ISC_LINK_INIT(timer
, link
);
345 timer
->magic
= TIMER_MAGIC
;
347 LOCK(&manager
->lock
);
350 * Note we don't have to lock the timer like we normally would because
351 * there are no external references to it yet.
354 if (type
!= isc_timertype_inactive
)
355 result
= schedule(timer
, &now
, ISC_TRUE
);
357 result
= ISC_R_SUCCESS
;
358 if (result
== ISC_R_SUCCESS
)
359 APPEND(manager
->timers
, timer
, link
);
361 UNLOCK(&manager
->lock
);
363 if (result
!= ISC_R_SUCCESS
) {
365 DESTROYLOCK(&timer
->lock
);
366 isc_task_detach(&timer
->task
);
367 isc_mem_put(manager
->mctx
, timer
, sizeof *timer
);
373 return (ISC_R_SUCCESS
);
377 isc_timer_reset(isc_timer_t
*timer
, isc_timertype_t type
,
378 isc_time_t
*expires
, isc_interval_t
*interval
,
382 isc_timermgr_t
*manager
;
386 * Change the timer's type, expires, and interval values to the given
387 * values. If 'purge' is ISC_TRUE, any pending events from this timer
388 * are purged from its task's event queue.
391 REQUIRE(VALID_TIMER(timer
));
392 manager
= timer
->manager
;
393 REQUIRE(VALID_MANAGER(manager
));
395 expires
= isc_time_epoch
;
396 if (interval
== NULL
)
397 interval
= isc_interval_zero
;
398 REQUIRE(type
== isc_timertype_inactive
||
399 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
404 if (type
!= isc_timertype_inactive
) {
405 result
= isc_time_now(&now
);
406 if (result
!= ISC_R_SUCCESS
) {
407 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
408 "isc_time_now() %s: %s",
409 isc_msgcat_get(isc_msgcat
,
413 isc_result_totext(result
));
414 return (ISC_R_UNEXPECTED
);
418 * We don't have to do this, but it keeps the compiler from
419 * complaining about "now" possibly being used without being
420 * set, even though it will never actually happen.
422 isc_time_settoepoch(&now
);
425 manager
= timer
->manager
;
427 LOCK(&manager
->lock
);
431 isc_task_purgerange(timer
->task
,
433 ISC_TIMEREVENT_FIRSTEVENT
,
434 ISC_TIMEREVENT_LASTEVENT
,
437 timer
->expires
= *expires
;
438 timer
->interval
= *interval
;
439 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
440 result
= isc_time_add(&now
, interval
, &timer
->idle
);
442 isc_time_settoepoch(&timer
->idle
);
443 result
= ISC_R_SUCCESS
;
446 if (result
== ISC_R_SUCCESS
) {
447 if (type
== isc_timertype_inactive
) {
449 result
= ISC_R_SUCCESS
;
451 result
= schedule(timer
, &now
, ISC_TRUE
);
454 UNLOCK(&timer
->lock
);
455 UNLOCK(&manager
->lock
);
461 isc_timer_touch(isc_timer_t
*timer
) {
466 * Set the last-touched time of 'timer' to the current time.
469 REQUIRE(VALID_TIMER(timer
));
476 * REQUIRE(timer->type == isc_timertype_once);
478 * but we cannot without locking the manager lock too, which we
482 result
= isc_time_now(&now
);
483 if (result
!= ISC_R_SUCCESS
) {
484 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
485 "isc_time_now() %s: %s",
486 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
487 ISC_MSG_FAILED
, "failed"),
488 isc_result_totext(result
));
489 result
= ISC_R_UNEXPECTED
;
491 result
= isc_time_add(&now
, &timer
->interval
, &timer
->idle
);
493 UNLOCK(&timer
->lock
);
499 isc_timer_attach(isc_timer_t
*timer
, isc_timer_t
**timerp
) {
501 * Attach *timerp to timer.
504 REQUIRE(VALID_TIMER(timer
));
505 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
509 UNLOCK(&timer
->lock
);
515 isc_timer_detach(isc_timer_t
**timerp
) {
517 isc_boolean_t free_timer
= ISC_FALSE
;
520 * Detach *timerp from its timer.
523 REQUIRE(timerp
!= NULL
);
525 REQUIRE(VALID_TIMER(timer
));
528 REQUIRE(timer
->references
> 0);
530 if (timer
->references
== 0)
531 free_timer
= ISC_TRUE
;
532 UNLOCK(&timer
->lock
);
541 dispatch(isc_timermgr_t
*manager
, isc_time_t
*now
) {
542 isc_boolean_t done
= ISC_FALSE
, post_event
, need_schedule
;
544 isc_eventtype_t type
= 0;
549 * The caller must be holding the manager lock.
552 while (manager
->nscheduled
> 0 && !done
) {
553 timer
= isc_heap_element(manager
->heap
, 1);
554 INSIST(timer
->type
!= isc_timertype_inactive
);
555 if (isc_time_compare(now
, &timer
->due
) >= 0) {
556 if (timer
->type
== isc_timertype_ticker
) {
557 type
= ISC_TIMEREVENT_TICK
;
558 post_event
= ISC_TRUE
;
559 need_schedule
= ISC_TRUE
;
560 } else if (!isc_time_isepoch(&timer
->expires
) &&
561 isc_time_compare(now
,
562 &timer
->expires
) >= 0) {
563 type
= ISC_TIMEREVENT_LIFE
;
564 post_event
= ISC_TRUE
;
565 need_schedule
= ISC_FALSE
;
566 } else if (!isc_time_isepoch(&timer
->idle
) &&
567 isc_time_compare(now
,
568 &timer
->idle
) >= 0) {
569 type
= ISC_TIMEREVENT_IDLE
;
570 post_event
= ISC_TRUE
;
571 need_schedule
= ISC_FALSE
;
574 * Idle timer has been touched; reschedule.
576 XTRACEID(isc_msgcat_get(isc_msgcat
,
581 post_event
= ISC_FALSE
;
582 need_schedule
= ISC_TRUE
;
586 XTRACEID(isc_msgcat_get(isc_msgcat
,
591 * XXX We could preallocate this event.
593 event
= isc_event_allocate(manager
->mctx
,
601 isc_task_send(timer
->task
, &event
);
603 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
604 isc_msgcat_get(isc_msgcat
,
606 ISC_MSG_EVENTNOTALLOC
,
612 isc_heap_delete(manager
->heap
, 1);
613 manager
->nscheduled
--;
616 result
= schedule(timer
, now
, ISC_FALSE
);
617 if (result
!= ISC_R_SUCCESS
)
618 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
619 isc_msgcat_get(isc_msgcat
,
623 "schedule timer: %u"),
627 manager
->due
= timer
->due
;
633 #ifdef ISC_PLATFORM_USETHREADS
634 static isc_threadresult_t
635 #ifdef _WIN32 /* XXXDCL */
639 isc_timermgr_t
*manager
= uap
;
643 LOCK(&manager
->lock
);
644 while (!manager
->done
) {
645 RUNTIME_CHECK(isc_time_now(&now
) == ISC_R_SUCCESS
);
647 XTRACETIME(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
651 dispatch(manager
, &now
);
653 if (manager
->nscheduled
> 0) {
654 XTRACETIME(isc_msgcat_get(isc_msgcat
,
659 result
= WAITUNTIL(&manager
->wakeup
, &manager
->lock
,
661 INSIST(result
== ISC_R_SUCCESS
||
662 result
== ISC_R_TIMEDOUT
);
664 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
665 ISC_MSG_WAIT
, "wait"));
666 WAIT(&manager
->wakeup
, &manager
->lock
);
668 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
669 ISC_MSG_WAKEUP
, "wakeup"));
671 UNLOCK(&manager
->lock
);
673 return ((isc_threadresult_t
)0);
675 #endif /* ISC_PLATFORM_USETHREADS */
678 sooner(void *v1
, void *v2
) {
679 isc_timer_t
*t1
, *t2
;
683 REQUIRE(VALID_TIMER(t1
));
684 REQUIRE(VALID_TIMER(t2
));
686 if (isc_time_compare(&t1
->due
, &t2
->due
) < 0)
692 set_index(void *what
, unsigned int index
) {
696 REQUIRE(VALID_TIMER(timer
));
698 timer
->index
= index
;
702 isc_timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
) {
703 isc_timermgr_t
*manager
;
707 * Create a timer manager.
710 REQUIRE(managerp
!= NULL
&& *managerp
== NULL
);
712 #ifndef ISC_PLATFORM_USETHREADS
713 if (timermgr
!= NULL
) {
715 *managerp
= timermgr
;
716 return (ISC_R_SUCCESS
);
718 #endif /* ISC_PLATFORM_USETHREADS */
720 manager
= isc_mem_get(mctx
, sizeof *manager
);
722 return (ISC_R_NOMEMORY
);
724 manager
->magic
= TIMER_MANAGER_MAGIC
;
725 manager
->mctx
= NULL
;
726 manager
->done
= ISC_FALSE
;
727 INIT_LIST(manager
->timers
);
728 manager
->nscheduled
= 0;
729 isc_time_settoepoch(&manager
->due
);
730 manager
->heap
= NULL
;
731 result
= isc_heap_create(mctx
, sooner
, set_index
, 0, &manager
->heap
);
732 if (result
!= ISC_R_SUCCESS
) {
733 INSIST(result
== ISC_R_NOMEMORY
);
734 isc_mem_put(mctx
, manager
, sizeof *manager
);
735 return (ISC_R_NOMEMORY
);
737 if (isc_mutex_init(&manager
->lock
) != ISC_R_SUCCESS
) {
738 isc_heap_destroy(&manager
->heap
);
739 isc_mem_put(mctx
, manager
, sizeof *manager
);
740 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
741 "isc_mutex_init() %s",
742 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
743 ISC_MSG_FAILED
, "failed"));
744 return (ISC_R_UNEXPECTED
);
746 isc_mem_attach(mctx
, &manager
->mctx
);
747 #ifdef ISC_PLATFORM_USETHREADS
748 if (isc_condition_init(&manager
->wakeup
) != ISC_R_SUCCESS
) {
749 isc_mem_detach(&manager
->mctx
);
750 DESTROYLOCK(&manager
->lock
);
751 isc_heap_destroy(&manager
->heap
);
752 isc_mem_put(mctx
, manager
, sizeof *manager
);
753 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
754 "isc_condition_init() %s",
755 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
756 ISC_MSG_FAILED
, "failed"));
757 return (ISC_R_UNEXPECTED
);
759 if (isc_thread_create(run
, manager
, &manager
->thread
) !=
761 isc_mem_detach(&manager
->mctx
);
762 (void)isc_condition_destroy(&manager
->wakeup
);
763 DESTROYLOCK(&manager
->lock
);
764 isc_heap_destroy(&manager
->heap
);
765 isc_mem_put(mctx
, manager
, sizeof *manager
);
766 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
767 "isc_thread_create() %s",
768 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
769 ISC_MSG_FAILED
, "failed"));
770 return (ISC_R_UNEXPECTED
);
772 #else /* ISC_PLATFORM_USETHREADS */
775 #endif /* ISC_PLATFORM_USETHREADS */
779 return (ISC_R_SUCCESS
);
783 isc_timermgr_destroy(isc_timermgr_t
**managerp
) {
784 isc_timermgr_t
*manager
;
788 * Destroy a timer manager.
791 REQUIRE(managerp
!= NULL
);
793 REQUIRE(VALID_MANAGER(manager
));
795 LOCK(&manager
->lock
);
797 #ifndef ISC_PLATFORM_USETHREADS
798 if (manager
->refs
> 1) {
800 UNLOCK(&manager
->lock
);
805 isc__timermgr_dispatch();
806 #endif /* ISC_PLATFORM_USETHREADS */
808 REQUIRE(EMPTY(manager
->timers
));
809 manager
->done
= ISC_TRUE
;
811 #ifdef ISC_PLATFORM_USETHREADS
812 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
813 ISC_MSG_SIGNALDESTROY
, "signal (destroy)"));
814 SIGNAL(&manager
->wakeup
);
815 #endif /* ISC_PLATFORM_USETHREADS */
817 UNLOCK(&manager
->lock
);
819 #ifdef ISC_PLATFORM_USETHREADS
821 * Wait for thread to exit.
823 if (isc_thread_join(manager
->thread
, NULL
) != ISC_R_SUCCESS
)
824 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
825 "isc_thread_join() %s",
826 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
827 ISC_MSG_FAILED
, "failed"));
828 #endif /* ISC_PLATFORM_USETHREADS */
833 #ifdef ISC_PLATFORM_USETHREADS
834 (void)isc_condition_destroy(&manager
->wakeup
);
835 #endif /* ISC_PLATFORM_USETHREADS */
836 DESTROYLOCK(&manager
->lock
);
837 isc_heap_destroy(&manager
->heap
);
839 mctx
= manager
->mctx
;
840 isc_mem_put(mctx
, manager
, sizeof *manager
);
841 isc_mem_detach(&mctx
);
846 #ifndef ISC_PLATFORM_USETHREADS
848 isc__timermgr_nextevent(isc_time_t
*when
) {
849 if (timermgr
== NULL
|| timermgr
->nscheduled
== 0)
850 return (ISC_R_NOTFOUND
);
851 *when
= timermgr
->due
;
852 return (ISC_R_SUCCESS
);
856 isc__timermgr_dispatch(void) {
858 if (timermgr
== NULL
)
859 return (ISC_R_NOTFOUND
);
861 dispatch(timermgr
, &now
);
862 return (ISC_R_SUCCESS
);
864 #endif /* ISC_PLATFORM_USETHREADS */