4 * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1998-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: timer.c,v 1.95 2009/10/05 17:30:49 fdupont Exp */
26 #include <isc/condition.h>
29 #include <isc/magic.h>
32 #include <isc/platform.h>
34 #include <isc/thread.h>
36 #include <isc/timer.h>
40 #include <openssl/err.h>
43 /* See task.c about the following definition: */
45 #ifdef ISC_PLATFORM_USETHREADS
46 #define USE_TIMER_THREAD
48 #define USE_SHARED_MANAGER
49 #endif /* ISC_PLATFORM_USETHREADS */
52 #ifndef USE_TIMER_THREAD
54 #endif /* USE_TIMER_THREAD */
56 #ifdef ISC_TIMER_TRACE
57 #define XTRACE(s) fprintf(stderr, "%s\n", (s))
58 #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
59 #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \
60 (d).seconds, (d).nanoseconds)
61 #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
62 (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
63 #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
64 (d).seconds, (d).nanoseconds)
67 #define XTRACEID(s, t)
68 #define XTRACETIME(s, d)
69 #define XTRACETIME2(s, d, n)
70 #define XTRACETIMER(s, t, d)
71 #endif /* ISC_TIMER_TRACE */
73 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
74 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
76 typedef struct isc__timer isc__timer_t
;
77 typedef struct isc__timermgr isc__timermgr_t
;
82 isc__timermgr_t
* manager
;
84 /*! Locked by timer lock. */
85 unsigned int references
;
87 /*! Locked by manager lock. */
90 isc_interval_t interval
;
92 isc_taskaction_t action
;
96 LINK(isc__timer_t
) link
;
99 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
100 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
102 struct isc__timermgr
{
104 isc_timermgr_t common
;
107 /* Locked by manager lock. */
109 LIST(isc__timer_t
) timers
;
110 unsigned int nscheduled
;
112 #ifdef USE_TIMER_THREAD
113 isc_condition_t wakeup
;
115 #endif /* USE_TIMER_THREAD */
116 #ifdef USE_SHARED_MANAGER
118 #endif /* USE_SHARED_MANAGER */
123 * The followings can be either static or public, depending on build
128 #define ISC_TIMERFUNC_SCOPE
130 #define ISC_TIMERFUNC_SCOPE static
133 ISC_TIMERFUNC_SCOPE isc_result_t
134 isc__timer_create(isc_timermgr_t
*manager
, isc_timertype_t type
,
135 isc_time_t
*expires
, isc_interval_t
*interval
,
136 isc_task_t
*task
, isc_taskaction_t action
, const void *arg
,
137 isc_timer_t
**timerp
);
138 ISC_TIMERFUNC_SCOPE isc_result_t
139 isc__timer_reset(isc_timer_t
*timer
, isc_timertype_t type
,
140 isc_time_t
*expires
, isc_interval_t
*interval
,
141 isc_boolean_t purge
);
142 ISC_TIMERFUNC_SCOPE isc_timertype_t
143 isc__timer_gettype(isc_timer_t
*timer
);
144 ISC_TIMERFUNC_SCOPE isc_result_t
145 isc__timer_touch(isc_timer_t
*timer
);
146 ISC_TIMERFUNC_SCOPE
void
147 isc__timer_attach(isc_timer_t
*timer0
, isc_timer_t
**timerp
);
148 ISC_TIMERFUNC_SCOPE
void
149 isc__timer_detach(isc_timer_t
**timerp
);
150 ISC_TIMERFUNC_SCOPE isc_result_t
151 isc__timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
);
152 ISC_TIMERFUNC_SCOPE
void
153 isc__timermgr_poke(isc_timermgr_t
*manager0
);
154 ISC_TIMERFUNC_SCOPE
void
155 isc__timermgr_destroy(isc_timermgr_t
**managerp
);
157 static struct isc__timermethods
{
158 isc_timermethods_t methods
;
161 * The following are defined just for avoiding unused static functions.
175 (void *)isc__timer_gettype
179 static struct isc__timermgrmethods
{
180 isc_timermgrmethods_t methods
;
182 void *poke
; /* see above */
184 } timermgrmethods
= {
186 isc__timermgr_destroy
,
191 (void *)isc__timermgr_poke
195 #ifdef USE_SHARED_MANAGER
197 * If the manager is supposed to be shared, there can be only one.
199 static isc__timermgr_t
*timermgr
= NULL
;
200 #endif /* USE_SHARED_MANAGER */
202 static inline isc_result_t
203 schedule(isc__timer_t
*timer
, isc_time_t
*now
, isc_boolean_t signal_ok
) {
205 isc__timermgr_t
*manager
;
208 #ifdef USE_TIMER_THREAD
209 isc_boolean_t timedwait
;
213 * Note: the caller must ensure locking.
216 REQUIRE(timer
->type
!= isc_timertype_inactive
);
218 #ifndef USE_TIMER_THREAD
220 #endif /* USE_TIMER_THREAD */
222 manager
= timer
->manager
;
224 #ifdef USE_TIMER_THREAD
226 * If the manager was timed wait, we may need to signal the
227 * manager to force a wakeup.
229 timedwait
= ISC_TF(manager
->nscheduled
> 0 &&
230 isc_time_seconds(&manager
->due
) != 0);
234 * Compute the new due time.
236 if (timer
->type
!= isc_timertype_once
) {
237 result
= isc_time_add(now
, &timer
->interval
, &due
);
238 if (result
!= ISC_R_SUCCESS
)
240 if (timer
->type
== isc_timertype_limited
&&
241 isc_time_compare(&timer
->expires
, &due
) < 0)
242 due
= timer
->expires
;
244 if (isc_time_isepoch(&timer
->idle
))
245 due
= timer
->expires
;
246 else if (isc_time_isepoch(&timer
->expires
))
248 else if (isc_time_compare(&timer
->idle
, &timer
->expires
) < 0)
251 due
= timer
->expires
;
255 * Schedule the timer.
258 if (timer
->index
> 0) {
262 cmp
= isc_time_compare(&due
, &timer
->due
);
266 isc_heap_increased(manager
->heap
, timer
->index
);
269 isc_heap_decreased(manager
->heap
, timer
->index
);
277 result
= isc_heap_insert(manager
->heap
, timer
);
278 if (result
!= ISC_R_SUCCESS
) {
279 INSIST(result
== ISC_R_NOMEMORY
);
280 return (ISC_R_NOMEMORY
);
282 manager
->nscheduled
++;
285 XTRACETIMER(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
286 ISC_MSG_SCHEDULE
, "schedule"), timer
, due
);
289 * If this timer is at the head of the queue, we need to ensure
290 * that we won't miss it if it has a more recent due time than
291 * the current "next" timer. We do this either by waking up the
292 * run thread, or explicitly setting the value in the manager.
294 #ifdef USE_TIMER_THREAD
297 * This is a temporary (probably) hack to fix a bug on tru64 5.1
298 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
299 * return when the time expires, so here, we check to see if
300 * we're 15 seconds or more behind, and if we are, we signal
301 * the dispatcher. This isn't such a bad idea as a general purpose
302 * watchdog, so perhaps we should just leave it in here.
304 if (signal_ok
&& timedwait
) {
305 isc_interval_t fifteen
;
308 isc_interval_set(&fifteen
, 15, 0);
309 result
= isc_time_add(&manager
->due
, &fifteen
, &then
);
311 if (result
== ISC_R_SUCCESS
&&
312 isc_time_compare(&then
, now
) < 0) {
313 SIGNAL(&manager
->wakeup
);
314 signal_ok
= ISC_FALSE
;
315 isc_log_write(isc_lctx
, ISC_LOGCATEGORY_GENERAL
,
316 ISC_LOGMODULE_TIMER
, ISC_LOG_WARNING
,
317 "*** POKED TIMER ***");
321 if (timer
->index
== 1 && signal_ok
) {
322 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
324 "signal (schedule)"));
325 SIGNAL(&manager
->wakeup
);
327 #else /* USE_TIMER_THREAD */
328 if (timer
->index
== 1 &&
329 isc_time_compare(&timer
->due
, &manager
->due
) < 0)
330 manager
->due
= timer
->due
;
331 #endif /* USE_TIMER_THREAD */
333 return (ISC_R_SUCCESS
);
337 deschedule(isc__timer_t
*timer
) {
338 isc_boolean_t need_wakeup
= ISC_FALSE
;
339 isc__timermgr_t
*manager
;
342 * The caller must ensure locking.
345 manager
= timer
->manager
;
346 if (timer
->index
> 0) {
347 if (timer
->index
== 1)
348 need_wakeup
= ISC_TRUE
;
349 isc_heap_delete(manager
->heap
, timer
->index
);
351 INSIST(manager
->nscheduled
> 0);
352 manager
->nscheduled
--;
353 #ifdef USE_TIMER_THREAD
355 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
356 ISC_MSG_SIGNALDESCHED
,
357 "signal (deschedule)"));
358 SIGNAL(&manager
->wakeup
);
360 #endif /* USE_TIMER_THREAD */
365 destroy(isc__timer_t
*timer
) {
366 isc__timermgr_t
*manager
= timer
->manager
;
369 * The caller must ensure it is safe to destroy the timer.
372 LOCK(&manager
->lock
);
374 (void)isc_task_purgerange(timer
->task
,
376 ISC_TIMEREVENT_FIRSTEVENT
,
377 ISC_TIMEREVENT_LASTEVENT
,
380 UNLINK(manager
->timers
, timer
, link
);
382 UNLOCK(&manager
->lock
);
384 isc_task_detach(&timer
->task
);
385 DESTROYLOCK(&timer
->lock
);
386 timer
->common
.impmagic
= 0;
387 timer
->common
.magic
= 0;
388 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
391 ISC_TIMERFUNC_SCOPE isc_result_t
392 isc__timer_create(isc_timermgr_t
*manager0
, isc_timertype_t type
,
393 isc_time_t
*expires
, isc_interval_t
*interval
,
394 isc_task_t
*task
, isc_taskaction_t action
, const void *arg
,
395 isc_timer_t
**timerp
)
397 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
403 * Create a new 'type' timer managed by 'manager'. The timers
404 * parameters are specified by 'expires' and 'interval'. Events
405 * will be posted to 'task' and when dispatched 'action' will be
406 * called with 'arg' as the arg value. The new timer is returned
410 REQUIRE(VALID_MANAGER(manager
));
411 REQUIRE(task
!= NULL
);
412 REQUIRE(action
!= NULL
);
414 expires
= isc_time_epoch
;
415 if (interval
== NULL
)
416 interval
= isc_interval_zero
;
417 REQUIRE(type
== isc_timertype_inactive
||
418 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
419 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
420 REQUIRE(type
!= isc_timertype_limited
||
421 !(isc_time_isepoch(expires
) || isc_interval_iszero(interval
)));
426 if (type
!= isc_timertype_inactive
) {
430 * We don't have to do this, but it keeps the compiler from
431 * complaining about "now" possibly being used without being
432 * set, even though it will never actually happen.
434 isc_time_settoepoch(&now
);
438 timer
= isc_mem_get(manager
->mctx
, sizeof(*timer
));
440 return (ISC_R_NOMEMORY
);
442 timer
->manager
= manager
;
443 timer
->references
= 1;
445 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
446 result
= isc_time_add(&now
, interval
, &timer
->idle
);
447 if (result
!= ISC_R_SUCCESS
) {
448 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
452 isc_time_settoepoch(&timer
->idle
);
455 timer
->expires
= *expires
;
456 timer
->interval
= *interval
;
458 isc_task_attach(task
, &timer
->task
);
459 timer
->action
= action
;
461 * Removing the const attribute from "arg" is the best of two
462 * evils here. If the timer->arg member is made const, then
463 * it affects a great many recipients of the timer event
464 * which did not pass in an "arg" that was truly const.
465 * Changing isc_timer_create() to not have "arg" prototyped as const,
466 * though, can cause compilers warnings for calls that *do*
467 * have a truly const arg. The caller will have to carefully
468 * keep track of whether arg started as a true const.
470 DE_CONST(arg
, timer
->arg
);
472 result
= isc_mutex_init(&timer
->lock
);
473 if (result
!= ISC_R_SUCCESS
) {
474 isc_task_detach(&timer
->task
);
475 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
478 ISC_LINK_INIT(timer
, link
);
479 timer
->common
.impmagic
= TIMER_MAGIC
;
480 timer
->common
.magic
= ISCAPI_TIMER_MAGIC
;
481 timer
->common
.methods
= (isc_timermethods_t
*)&timermethods
;
483 LOCK(&manager
->lock
);
486 * Note we don't have to lock the timer like we normally would because
487 * there are no external references to it yet.
490 if (type
!= isc_timertype_inactive
)
491 result
= schedule(timer
, &now
, ISC_TRUE
);
493 result
= ISC_R_SUCCESS
;
494 if (result
== ISC_R_SUCCESS
)
495 APPEND(manager
->timers
, timer
, link
);
497 UNLOCK(&manager
->lock
);
499 if (result
!= ISC_R_SUCCESS
) {
500 timer
->common
.impmagic
= 0;
501 timer
->common
.magic
= 0;
502 DESTROYLOCK(&timer
->lock
);
503 isc_task_detach(&timer
->task
);
504 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
508 *timerp
= (isc_timer_t
*)timer
;
510 return (ISC_R_SUCCESS
);
513 ISC_TIMERFUNC_SCOPE isc_result_t
514 isc__timer_reset(isc_timer_t
*timer0
, isc_timertype_t type
,
515 isc_time_t
*expires
, isc_interval_t
*interval
,
518 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
520 isc__timermgr_t
*manager
;
524 * Change the timer's type, expires, and interval values to the given
525 * values. If 'purge' is ISC_TRUE, any pending events from this timer
526 * are purged from its task's event queue.
529 REQUIRE(VALID_TIMER(timer
));
530 manager
= timer
->manager
;
531 REQUIRE(VALID_MANAGER(manager
));
533 expires
= isc_time_epoch
;
534 if (interval
== NULL
)
535 interval
= isc_interval_zero
;
536 REQUIRE(type
== isc_timertype_inactive
||
537 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
538 REQUIRE(type
!= isc_timertype_limited
||
539 !(isc_time_isepoch(expires
) || isc_interval_iszero(interval
)));
544 if (type
!= isc_timertype_inactive
) {
548 * We don't have to do this, but it keeps the compiler from
549 * complaining about "now" possibly being used without being
550 * set, even though it will never actually happen.
552 isc_time_settoepoch(&now
);
555 manager
= timer
->manager
;
557 LOCK(&manager
->lock
);
561 (void)isc_task_purgerange(timer
->task
,
563 ISC_TIMEREVENT_FIRSTEVENT
,
564 ISC_TIMEREVENT_LASTEVENT
,
567 timer
->expires
= *expires
;
568 timer
->interval
= *interval
;
569 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
570 result
= isc_time_add(&now
, interval
, &timer
->idle
);
572 isc_time_settoepoch(&timer
->idle
);
573 result
= ISC_R_SUCCESS
;
576 if (result
== ISC_R_SUCCESS
) {
577 if (type
== isc_timertype_inactive
) {
579 result
= ISC_R_SUCCESS
;
581 result
= schedule(timer
, &now
, ISC_TRUE
);
584 UNLOCK(&timer
->lock
);
585 UNLOCK(&manager
->lock
);
590 ISC_TIMERFUNC_SCOPE isc_timertype_t
591 isc__timer_gettype(isc_timer_t
*timer0
) {
592 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
595 REQUIRE(VALID_TIMER(timer
));
599 UNLOCK(&timer
->lock
);
604 ISC_TIMERFUNC_SCOPE isc_result_t
605 isc__timer_touch(isc_timer_t
*timer0
) {
606 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
611 * Set the last-touched time of 'timer' to the current time.
614 REQUIRE(VALID_TIMER(timer
));
621 * REQUIRE(timer->type == isc_timertype_once);
623 * but we cannot without locking the manager lock too, which we
628 result
= isc_time_add(&now
, &timer
->interval
, &timer
->idle
);
630 UNLOCK(&timer
->lock
);
635 ISC_TIMERFUNC_SCOPE
void
636 isc__timer_attach(isc_timer_t
*timer0
, isc_timer_t
**timerp
) {
637 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
640 * Attach *timerp to timer.
643 REQUIRE(VALID_TIMER(timer
));
644 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
648 UNLOCK(&timer
->lock
);
650 *timerp
= (isc_timer_t
*)timer
;
653 ISC_TIMERFUNC_SCOPE
void
654 isc__timer_detach(isc_timer_t
**timerp
) {
656 isc_boolean_t free_timer
= ISC_FALSE
;
659 * Detach *timerp from its timer.
662 REQUIRE(timerp
!= NULL
);
663 timer
= (isc__timer_t
*)*timerp
;
664 REQUIRE(VALID_TIMER(timer
));
667 REQUIRE(timer
->references
> 0);
669 if (timer
->references
== 0)
670 free_timer
= ISC_TRUE
;
671 UNLOCK(&timer
->lock
);
680 dispatch(isc__timermgr_t
*manager
, isc_time_t
*now
) {
681 isc_boolean_t done
= ISC_FALSE
, post_event
, need_schedule
;
682 isc_timerevent_t
*event
;
683 isc_eventtype_t type
= 0;
689 * The caller must be holding the manager lock.
692 while (manager
->nscheduled
> 0 && !done
) {
693 timer
= isc_heap_element(manager
->heap
, 1);
694 INSIST(timer
->type
!= isc_timertype_inactive
);
695 if (isc_time_compare(now
, &timer
->due
) >= 0) {
696 if (timer
->type
== isc_timertype_ticker
) {
697 type
= ISC_TIMEREVENT_TICK
;
698 post_event
= ISC_TRUE
;
699 need_schedule
= ISC_TRUE
;
700 } else if (timer
->type
== isc_timertype_limited
) {
702 cmp
= isc_time_compare(now
, &timer
->expires
);
704 type
= ISC_TIMEREVENT_LIFE
;
705 post_event
= ISC_TRUE
;
706 need_schedule
= ISC_FALSE
;
708 type
= ISC_TIMEREVENT_TICK
;
709 post_event
= ISC_TRUE
;
710 need_schedule
= ISC_TRUE
;
712 } else if (!isc_time_isepoch(&timer
->expires
) &&
713 isc_time_compare(now
,
714 &timer
->expires
) >= 0) {
715 type
= ISC_TIMEREVENT_LIFE
;
716 post_event
= ISC_TRUE
;
717 need_schedule
= ISC_FALSE
;
722 if (!isc_time_isepoch(&timer
->idle
) &&
723 isc_time_compare(now
,
724 &timer
->idle
) >= 0) {
727 UNLOCK(&timer
->lock
);
729 type
= ISC_TIMEREVENT_IDLE
;
730 post_event
= ISC_TRUE
;
731 need_schedule
= ISC_FALSE
;
734 * Idle timer has been touched;
737 XTRACEID(isc_msgcat_get(isc_msgcat
,
742 post_event
= ISC_FALSE
;
743 need_schedule
= ISC_TRUE
;
748 XTRACEID(isc_msgcat_get(isc_msgcat
,
753 * XXX We could preallocate this event.
755 event
= (isc_timerevent_t
*)isc_event_allocate(manager
->mctx
,
763 event
->due
= timer
->due
;
764 isc_task_send(timer
->task
,
765 ISC_EVENT_PTR(&event
));
767 UNEXPECTED_ERROR(__FILE__
, __LINE__
, "%s",
768 isc_msgcat_get(isc_msgcat
,
770 ISC_MSG_EVENTNOTALLOC
,
776 isc_heap_delete(manager
->heap
, 1);
777 manager
->nscheduled
--;
780 result
= schedule(timer
, now
, ISC_FALSE
);
781 if (result
!= ISC_R_SUCCESS
)
782 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
784 isc_msgcat_get(isc_msgcat
,
792 manager
->due
= timer
->due
;
798 #ifdef USE_TIMER_THREAD
799 static isc_threadresult_t
800 #ifdef _WIN32 /* XXXDCL */
804 isc__timermgr_t
*manager
= uap
;
808 LOCK(&manager
->lock
);
809 while (!manager
->done
) {
812 XTRACETIME(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
816 dispatch(manager
, &now
);
818 if (manager
->nscheduled
> 0) {
819 XTRACETIME2(isc_msgcat_get(isc_msgcat
,
824 result
= WAITUNTIL(&manager
->wakeup
, &manager
->lock
, &manager
->due
);
825 INSIST(result
== ISC_R_SUCCESS
||
826 result
== ISC_R_TIMEDOUT
);
828 XTRACETIME(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
829 ISC_MSG_WAIT
, "wait"), now
);
830 WAIT(&manager
->wakeup
, &manager
->lock
);
832 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
833 ISC_MSG_WAKEUP
, "wakeup"));
835 UNLOCK(&manager
->lock
);
841 return ((isc_threadresult_t
)0);
843 #endif /* USE_TIMER_THREAD */
846 sooner(void *v1
, void *v2
) {
847 isc__timer_t
*t1
, *t2
;
851 REQUIRE(VALID_TIMER(t1
));
852 REQUIRE(VALID_TIMER(t2
));
854 if (isc_time_compare(&t1
->due
, &t2
->due
) < 0)
860 set_index(void *what
, unsigned int index
) {
864 REQUIRE(VALID_TIMER(timer
));
866 timer
->index
= index
;
869 ISC_TIMERFUNC_SCOPE isc_result_t
870 isc__timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
) {
871 isc__timermgr_t
*manager
;
875 * Create a timer manager.
878 REQUIRE(managerp
!= NULL
&& *managerp
== NULL
);
880 #ifdef USE_SHARED_MANAGER
881 if (timermgr
!= NULL
) {
883 *managerp
= (isc_timermgr_t
*)timermgr
;
884 return (ISC_R_SUCCESS
);
886 #endif /* USE_SHARED_MANAGER */
888 manager
= isc_mem_get(mctx
, sizeof(*manager
));
890 return (ISC_R_NOMEMORY
);
892 manager
->common
.impmagic
= TIMER_MANAGER_MAGIC
;
893 manager
->common
.magic
= ISCAPI_TIMERMGR_MAGIC
;
894 manager
->common
.methods
= (isc_timermgrmethods_t
*)&timermgrmethods
;
895 manager
->mctx
= NULL
;
896 manager
->done
= ISC_FALSE
;
897 INIT_LIST(manager
->timers
);
898 manager
->nscheduled
= 0;
899 isc_time_settoepoch(&manager
->due
);
900 manager
->heap
= NULL
;
901 result
= isc_heap_create(mctx
, sooner
, set_index
, 0, &manager
->heap
);
902 if (result
!= ISC_R_SUCCESS
) {
903 INSIST(result
== ISC_R_NOMEMORY
);
904 isc_mem_put(mctx
, manager
, sizeof(*manager
));
905 return (ISC_R_NOMEMORY
);
907 result
= isc_mutex_init(&manager
->lock
);
908 if (result
!= ISC_R_SUCCESS
) {
909 isc_heap_destroy(&manager
->heap
);
910 isc_mem_put(mctx
, manager
, sizeof(*manager
));
913 isc_mem_attach(mctx
, &manager
->mctx
);
914 #ifdef USE_TIMER_THREAD
915 if (isc_condition_init(&manager
->wakeup
) != ISC_R_SUCCESS
) {
916 isc_mem_detach(&manager
->mctx
);
917 DESTROYLOCK(&manager
->lock
);
918 isc_heap_destroy(&manager
->heap
);
919 isc_mem_put(mctx
, manager
, sizeof(*manager
));
920 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
921 "isc_condition_init() %s",
922 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
923 ISC_MSG_FAILED
, "failed"));
924 return (ISC_R_UNEXPECTED
);
926 if (isc_thread_create(run
, manager
, &manager
->thread
) !=
928 isc_mem_detach(&manager
->mctx
);
929 (void)isc_condition_destroy(&manager
->wakeup
);
930 DESTROYLOCK(&manager
->lock
);
931 isc_heap_destroy(&manager
->heap
);
932 isc_mem_put(mctx
, manager
, sizeof(*manager
));
933 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
934 "isc_thread_create() %s",
935 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
936 ISC_MSG_FAILED
, "failed"));
937 return (ISC_R_UNEXPECTED
);
940 #ifdef USE_SHARED_MANAGER
943 #endif /* USE_SHARED_MANAGER */
945 *managerp
= (isc_timermgr_t
*)manager
;
947 return (ISC_R_SUCCESS
);
950 ISC_TIMERFUNC_SCOPE
void
951 isc__timermgr_poke(isc_timermgr_t
*manager0
) {
952 #ifdef USE_TIMER_THREAD
953 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
955 REQUIRE(VALID_MANAGER(manager
));
957 SIGNAL(&manager
->wakeup
);
963 ISC_TIMERFUNC_SCOPE
void
964 isc__timermgr_destroy(isc_timermgr_t
**managerp
) {
965 isc__timermgr_t
*manager
;
969 * Destroy a timer manager.
972 REQUIRE(managerp
!= NULL
);
973 manager
= (isc__timermgr_t
*)*managerp
;
974 REQUIRE(VALID_MANAGER(manager
));
976 LOCK(&manager
->lock
);
978 #ifdef USE_SHARED_MANAGER
979 if (manager
->refs
> 1) {
981 UNLOCK(&manager
->lock
);
985 #endif /* USE_SHARED_MANAGER */
987 #ifndef USE_TIMER_THREAD
988 isc__timermgr_dispatch((isc_timermgr_t
*)manager
);
991 REQUIRE(EMPTY(manager
->timers
));
992 manager
->done
= ISC_TRUE
;
994 #ifdef USE_TIMER_THREAD
995 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
996 ISC_MSG_SIGNALDESTROY
, "signal (destroy)"));
997 SIGNAL(&manager
->wakeup
);
998 #endif /* USE_TIMER_THREAD */
1000 UNLOCK(&manager
->lock
);
1002 #ifdef USE_TIMER_THREAD
1004 * Wait for thread to exit.
1006 if (isc_thread_join(manager
->thread
, NULL
) != ISC_R_SUCCESS
)
1007 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
1008 "isc_thread_join() %s",
1009 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
1010 ISC_MSG_FAILED
, "failed"));
1011 #endif /* USE_TIMER_THREAD */
1016 #ifdef USE_TIMER_THREAD
1017 (void)isc_condition_destroy(&manager
->wakeup
);
1018 #endif /* USE_TIMER_THREAD */
1019 DESTROYLOCK(&manager
->lock
);
1020 isc_heap_destroy(&manager
->heap
);
1021 manager
->common
.impmagic
= 0;
1022 manager
->common
.magic
= 0;
1023 mctx
= manager
->mctx
;
1024 isc_mem_put(mctx
, manager
, sizeof(*manager
));
1025 isc_mem_detach(&mctx
);
1029 #ifdef USE_SHARED_MANAGER
1034 #ifndef USE_TIMER_THREAD
1036 isc__timermgr_nextevent(isc_timermgr_t
*manager0
, isc_time_t
*when
) {
1037 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
1039 #ifdef USE_SHARED_MANAGER
1040 if (manager
== NULL
)
1043 if (manager
== NULL
|| manager
->nscheduled
== 0)
1044 return (ISC_R_NOTFOUND
);
1045 *when
= manager
->due
;
1046 return (ISC_R_SUCCESS
);
1050 isc__timermgr_dispatch(isc_timermgr_t
*manager0
) {
1051 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
1054 #ifdef USE_SHARED_MANAGER
1055 if (manager
== NULL
)
1058 if (manager
== NULL
)
1061 dispatch(manager
, &now
);
1063 #endif /* USE_TIMER_THREAD */
1065 #ifdef USE_TIMERIMPREGISTER
1067 isc__timer_register() {
1068 return (isc_timer_register(isc__timermgr_create
));