1 /* $NetBSD: timer.c,v 1.10 2014/12/10 04:37:59 christos Exp $ */
4 * Copyright (C) 2004, 2005, 2007-2009, 2011-2014 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.
27 #include <isc/condition.h>
30 #include <isc/magic.h>
34 #include <isc/platform.h>
36 #include <isc/thread.h>
38 #include <isc/timer.h>
42 #include <openssl/err.h>
45 /* See task.c about the following definition: */
46 #ifdef ISC_PLATFORM_USETHREADS
47 #define USE_TIMER_THREAD
49 #define USE_SHARED_MANAGER
50 #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 following are intended for internal use (indicated by "isc__"
124 * prefix) but are not declared as static, allowing direct access from
129 isc__timer_create(isc_timermgr_t
*manager
, isc_timertype_t type
,
130 const isc_time_t
*expires
, const isc_interval_t
*interval
,
131 isc_task_t
*task
, isc_taskaction_t action
, void *arg
,
132 isc_timer_t
**timerp
);
134 isc__timer_reset(isc_timer_t
*timer
, isc_timertype_t type
,
135 const isc_time_t
*expires
, const isc_interval_t
*interval
,
136 isc_boolean_t purge
);
138 isc_timer_gettype(isc_timer_t
*timer
);
140 isc__timer_touch(isc_timer_t
*timer
);
142 isc__timer_attach(isc_timer_t
*timer0
, isc_timer_t
**timerp
);
144 isc__timer_detach(isc_timer_t
**timerp
);
146 isc__timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
);
148 isc_timermgr_poke(isc_timermgr_t
*manager0
);
150 isc__timermgr_destroy(isc_timermgr_t
**managerp
);
152 static struct isc__timermethods
{
153 isc_timermethods_t methods
;
156 * The following are defined just for avoiding unused static functions.
166 (void *)isc_timer_gettype
169 static struct isc__timermgrmethods
{
170 isc_timermgrmethods_t methods
;
171 void *poke
; /* see above */
172 } timermgrmethods
= {
174 isc__timermgr_destroy
,
177 (void *)isc_timermgr_poke
180 #ifdef USE_SHARED_MANAGER
182 * If the manager is supposed to be shared, there can be only one.
184 static isc__timermgr_t
*timermgr
= NULL
;
185 #endif /* USE_SHARED_MANAGER */
187 static inline isc_result_t
188 schedule(isc__timer_t
*timer
, isc_time_t
*now
, isc_boolean_t signal_ok
) {
190 isc__timermgr_t
*manager
;
193 #ifdef USE_TIMER_THREAD
194 isc_boolean_t timedwait
;
198 * Note: the caller must ensure locking.
201 REQUIRE(timer
->type
!= isc_timertype_inactive
);
203 #ifndef USE_TIMER_THREAD
205 #endif /* USE_TIMER_THREAD */
207 manager
= timer
->manager
;
209 #ifdef USE_TIMER_THREAD
211 * If the manager was timed wait, we may need to signal the
212 * manager to force a wakeup.
214 timedwait
= ISC_TF(manager
->nscheduled
> 0 &&
215 isc_time_seconds(&manager
->due
) != 0);
219 * Compute the new due time.
221 if (timer
->type
!= isc_timertype_once
) {
222 result
= isc_time_add(now
, &timer
->interval
, &due
);
223 if (result
!= ISC_R_SUCCESS
)
225 if (timer
->type
== isc_timertype_limited
&&
226 isc_time_compare(&timer
->expires
, &due
) < 0)
227 due
= timer
->expires
;
229 if (isc_time_isepoch(&timer
->idle
))
230 due
= timer
->expires
;
231 else if (isc_time_isepoch(&timer
->expires
))
233 else if (isc_time_compare(&timer
->idle
, &timer
->expires
) < 0)
236 due
= timer
->expires
;
240 * Schedule the timer.
243 if (timer
->index
> 0) {
247 cmp
= isc_time_compare(&due
, &timer
->due
);
251 isc_heap_increased(manager
->heap
, timer
->index
);
254 isc_heap_decreased(manager
->heap
, timer
->index
);
262 result
= isc_heap_insert(manager
->heap
, timer
);
263 if (result
!= ISC_R_SUCCESS
) {
264 INSIST(result
== ISC_R_NOMEMORY
);
265 return (ISC_R_NOMEMORY
);
267 manager
->nscheduled
++;
270 XTRACETIMER(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
271 ISC_MSG_SCHEDULE
, "schedule"), timer
, due
);
274 * If this timer is at the head of the queue, we need to ensure
275 * that we won't miss it if it has a more recent due time than
276 * the current "next" timer. We do this either by waking up the
277 * run thread, or explicitly setting the value in the manager.
279 #ifdef USE_TIMER_THREAD
282 * This is a temporary (probably) hack to fix a bug on tru64 5.1
283 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
284 * return when the time expires, so here, we check to see if
285 * we're 15 seconds or more behind, and if we are, we signal
286 * the dispatcher. This isn't such a bad idea as a general purpose
287 * watchdog, so perhaps we should just leave it in here.
289 if (signal_ok
&& timedwait
) {
290 isc_interval_t fifteen
;
293 isc_interval_set(&fifteen
, 15, 0);
294 result
= isc_time_add(&manager
->due
, &fifteen
, &then
);
296 if (result
== ISC_R_SUCCESS
&&
297 isc_time_compare(&then
, now
) < 0) {
298 SIGNAL(&manager
->wakeup
);
299 signal_ok
= ISC_FALSE
;
300 isc_log_write(isc_lctx
, ISC_LOGCATEGORY_GENERAL
,
301 ISC_LOGMODULE_TIMER
, ISC_LOG_WARNING
,
302 "*** POKED TIMER ***");
306 if (timer
->index
== 1 && signal_ok
) {
307 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
309 "signal (schedule)"));
310 SIGNAL(&manager
->wakeup
);
312 #else /* USE_TIMER_THREAD */
313 if (timer
->index
== 1 &&
314 isc_time_compare(&timer
->due
, &manager
->due
) < 0)
315 manager
->due
= timer
->due
;
316 #endif /* USE_TIMER_THREAD */
318 return (ISC_R_SUCCESS
);
322 deschedule(isc__timer_t
*timer
) {
323 #ifdef USE_TIMER_THREAD
324 isc_boolean_t need_wakeup
= ISC_FALSE
;
326 isc__timermgr_t
*manager
;
329 * The caller must ensure locking.
332 manager
= timer
->manager
;
333 if (timer
->index
> 0) {
334 #ifdef USE_TIMER_THREAD
335 if (timer
->index
== 1)
336 need_wakeup
= ISC_TRUE
;
338 isc_heap_delete(manager
->heap
, timer
->index
);
340 INSIST(manager
->nscheduled
> 0);
341 manager
->nscheduled
--;
342 #ifdef USE_TIMER_THREAD
344 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
345 ISC_MSG_SIGNALDESCHED
,
346 "signal (deschedule)"));
347 SIGNAL(&manager
->wakeup
);
349 #endif /* USE_TIMER_THREAD */
354 destroy(isc__timer_t
*timer
) {
355 isc__timermgr_t
*manager
= timer
->manager
;
358 * The caller must ensure it is safe to destroy the timer.
361 LOCK(&manager
->lock
);
363 (void)isc_task_purgerange(timer
->task
,
365 ISC_TIMEREVENT_FIRSTEVENT
,
366 ISC_TIMEREVENT_LASTEVENT
,
369 UNLINK(manager
->timers
, timer
, link
);
371 UNLOCK(&manager
->lock
);
373 isc_task_detach(&timer
->task
);
374 DESTROYLOCK(&timer
->lock
);
375 timer
->common
.impmagic
= 0;
376 timer
->common
.magic
= 0;
377 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
381 isc__timer_create(isc_timermgr_t
*manager0
, isc_timertype_t type
,
382 const isc_time_t
*expires
, const isc_interval_t
*interval
,
383 isc_task_t
*task
, isc_taskaction_t action
, void *arg
,
384 isc_timer_t
**timerp
)
386 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
392 * Create a new 'type' timer managed by 'manager'. The timers
393 * parameters are specified by 'expires' and 'interval'. Events
394 * will be posted to 'task' and when dispatched 'action' will be
395 * called with 'arg' as the arg value. The new timer is returned
399 REQUIRE(VALID_MANAGER(manager
));
400 REQUIRE(task
!= NULL
);
401 REQUIRE(action
!= NULL
);
403 expires
= isc_time_epoch
;
404 if (interval
== NULL
)
405 interval
= isc_interval_zero
;
406 REQUIRE(type
== isc_timertype_inactive
||
407 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
408 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
409 REQUIRE(type
!= isc_timertype_limited
||
410 !(isc_time_isepoch(expires
) || isc_interval_iszero(interval
)));
415 if (type
!= isc_timertype_inactive
) {
419 * We don't have to do this, but it keeps the compiler from
420 * complaining about "now" possibly being used without being
421 * set, even though it will never actually happen.
423 isc_time_settoepoch(&now
);
427 timer
= isc_mem_get(manager
->mctx
, sizeof(*timer
));
429 return (ISC_R_NOMEMORY
);
431 timer
->manager
= manager
;
432 timer
->references
= 1;
434 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
435 result
= isc_time_add(&now
, interval
, &timer
->idle
);
436 if (result
!= ISC_R_SUCCESS
) {
437 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
441 isc_time_settoepoch(&timer
->idle
);
444 timer
->expires
= *expires
;
445 timer
->interval
= *interval
;
447 isc_task_attach(task
, &timer
->task
);
448 timer
->action
= action
;
450 * Removing the const attribute from "arg" is the best of two
451 * evils here. If the timer->arg member is made const, then
452 * it affects a great many recipients of the timer event
453 * which did not pass in an "arg" that was truly const.
454 * Changing isc_timer_create() to not have "arg" prototyped as const,
455 * though, can cause compilers warnings for calls that *do*
456 * have a truly const arg. The caller will have to carefully
457 * keep track of whether arg started as a true const.
459 DE_CONST(arg
, timer
->arg
);
461 result
= isc_mutex_init(&timer
->lock
);
462 if (result
!= ISC_R_SUCCESS
) {
463 isc_task_detach(&timer
->task
);
464 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
467 ISC_LINK_INIT(timer
, link
);
468 timer
->common
.impmagic
= TIMER_MAGIC
;
469 timer
->common
.magic
= ISCAPI_TIMER_MAGIC
;
470 timer
->common
.methods
= (isc_timermethods_t
*)&timermethods
;
472 LOCK(&manager
->lock
);
475 * Note we don't have to lock the timer like we normally would because
476 * there are no external references to it yet.
479 if (type
!= isc_timertype_inactive
)
480 result
= schedule(timer
, &now
, ISC_TRUE
);
482 result
= ISC_R_SUCCESS
;
483 if (result
== ISC_R_SUCCESS
)
484 APPEND(manager
->timers
, timer
, link
);
486 UNLOCK(&manager
->lock
);
488 if (result
!= ISC_R_SUCCESS
) {
489 timer
->common
.impmagic
= 0;
490 timer
->common
.magic
= 0;
491 DESTROYLOCK(&timer
->lock
);
492 isc_task_detach(&timer
->task
);
493 isc_mem_put(manager
->mctx
, timer
, sizeof(*timer
));
497 *timerp
= (isc_timer_t
*)timer
;
499 return (ISC_R_SUCCESS
);
503 isc__timer_reset(isc_timer_t
*timer0
, isc_timertype_t type
,
504 const isc_time_t
*expires
, const isc_interval_t
*interval
,
507 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
509 isc__timermgr_t
*manager
;
513 * Change the timer's type, expires, and interval values to the given
514 * values. If 'purge' is ISC_TRUE, any pending events from this timer
515 * are purged from its task's event queue.
518 REQUIRE(VALID_TIMER(timer
));
519 manager
= timer
->manager
;
520 REQUIRE(VALID_MANAGER(manager
));
523 expires
= isc_time_epoch
;
524 if (interval
== NULL
)
525 interval
= isc_interval_zero
;
526 REQUIRE(type
== isc_timertype_inactive
||
527 !(isc_time_isepoch(expires
) && isc_interval_iszero(interval
)));
528 REQUIRE(type
!= isc_timertype_limited
||
529 !(isc_time_isepoch(expires
) || isc_interval_iszero(interval
)));
534 if (type
!= isc_timertype_inactive
) {
538 * We don't have to do this, but it keeps the compiler from
539 * complaining about "now" possibly being used without being
540 * set, even though it will never actually happen.
542 isc_time_settoepoch(&now
);
545 LOCK(&manager
->lock
);
549 (void)isc_task_purgerange(timer
->task
,
551 ISC_TIMEREVENT_FIRSTEVENT
,
552 ISC_TIMEREVENT_LASTEVENT
,
555 timer
->expires
= *expires
;
556 timer
->interval
= *interval
;
557 if (type
== isc_timertype_once
&& !isc_interval_iszero(interval
)) {
558 result
= isc_time_add(&now
, interval
, &timer
->idle
);
560 isc_time_settoepoch(&timer
->idle
);
561 result
= ISC_R_SUCCESS
;
564 if (result
== ISC_R_SUCCESS
) {
565 if (type
== isc_timertype_inactive
) {
567 result
= ISC_R_SUCCESS
;
569 result
= schedule(timer
, &now
, ISC_TRUE
);
572 UNLOCK(&timer
->lock
);
573 UNLOCK(&manager
->lock
);
579 isc_timer_gettype(isc_timer_t
*timer0
) {
580 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
583 REQUIRE(VALID_TIMER(timer
));
587 UNLOCK(&timer
->lock
);
593 isc__timer_touch(isc_timer_t
*timer0
) {
594 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
599 * Set the last-touched time of 'timer' to the current time.
602 REQUIRE(VALID_TIMER(timer
));
609 * REQUIRE(timer->type == isc_timertype_once);
611 * but we cannot without locking the manager lock too, which we
616 result
= isc_time_add(&now
, &timer
->interval
, &timer
->idle
);
618 UNLOCK(&timer
->lock
);
624 isc__timer_attach(isc_timer_t
*timer0
, isc_timer_t
**timerp
) {
625 isc__timer_t
*timer
= (isc__timer_t
*)timer0
;
628 * Attach *timerp to timer.
631 REQUIRE(VALID_TIMER(timer
));
632 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
636 UNLOCK(&timer
->lock
);
638 *timerp
= (isc_timer_t
*)timer
;
642 isc__timer_detach(isc_timer_t
**timerp
) {
644 isc_boolean_t free_timer
= ISC_FALSE
;
647 * Detach *timerp from its timer.
650 REQUIRE(timerp
!= NULL
);
651 timer
= (isc__timer_t
*)*timerp
;
652 REQUIRE(VALID_TIMER(timer
));
655 REQUIRE(timer
->references
> 0);
657 if (timer
->references
== 0)
658 free_timer
= ISC_TRUE
;
659 UNLOCK(&timer
->lock
);
668 dispatch(isc__timermgr_t
*manager
, isc_time_t
*now
) {
669 isc_boolean_t done
= ISC_FALSE
, post_event
, need_schedule
;
670 isc_timerevent_t
*event
;
671 isc_eventtype_t type
= 0;
677 * The caller must be holding the manager lock.
680 while (manager
->nscheduled
> 0 && !done
) {
681 timer
= isc_heap_element(manager
->heap
, 1);
682 INSIST(timer
!= NULL
&& timer
->type
!= isc_timertype_inactive
);
683 if (isc_time_compare(now
, &timer
->due
) >= 0) {
684 if (timer
->type
== isc_timertype_ticker
) {
685 type
= ISC_TIMEREVENT_TICK
;
686 post_event
= ISC_TRUE
;
687 need_schedule
= ISC_TRUE
;
688 } else if (timer
->type
== isc_timertype_limited
) {
690 cmp
= isc_time_compare(now
, &timer
->expires
);
692 type
= ISC_TIMEREVENT_LIFE
;
693 post_event
= ISC_TRUE
;
694 need_schedule
= ISC_FALSE
;
696 type
= ISC_TIMEREVENT_TICK
;
697 post_event
= ISC_TRUE
;
698 need_schedule
= ISC_TRUE
;
700 } else if (!isc_time_isepoch(&timer
->expires
) &&
701 isc_time_compare(now
,
702 &timer
->expires
) >= 0) {
703 type
= ISC_TIMEREVENT_LIFE
;
704 post_event
= ISC_TRUE
;
705 need_schedule
= ISC_FALSE
;
710 if (!isc_time_isepoch(&timer
->idle
) &&
711 isc_time_compare(now
,
712 &timer
->idle
) >= 0) {
715 UNLOCK(&timer
->lock
);
717 type
= ISC_TIMEREVENT_IDLE
;
718 post_event
= ISC_TRUE
;
719 need_schedule
= ISC_FALSE
;
722 * Idle timer has been touched;
725 XTRACEID(isc_msgcat_get(isc_msgcat
,
730 post_event
= ISC_FALSE
;
731 need_schedule
= ISC_TRUE
;
736 XTRACEID(isc_msgcat_get(isc_msgcat
,
741 * XXX We could preallocate this event.
743 event
= (isc_timerevent_t
*)isc_event_allocate(manager
->mctx
,
751 event
->due
= timer
->due
;
752 isc_task_send(timer
->task
,
753 ISC_EVENT_PTR(&event
));
755 UNEXPECTED_ERROR(__FILE__
, __LINE__
, "%s",
756 isc_msgcat_get(isc_msgcat
,
758 ISC_MSG_EVENTNOTALLOC
,
764 isc_heap_delete(manager
->heap
, 1);
765 manager
->nscheduled
--;
768 result
= schedule(timer
, now
, ISC_FALSE
);
769 if (result
!= ISC_R_SUCCESS
)
770 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
772 isc_msgcat_get(isc_msgcat
,
780 manager
->due
= timer
->due
;
786 #ifdef USE_TIMER_THREAD
787 static isc_threadresult_t
788 #ifdef _WIN32 /* XXXDCL */
792 isc__timermgr_t
*manager
= uap
;
796 LOCK(&manager
->lock
);
797 while (!manager
->done
) {
800 XTRACETIME(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
804 dispatch(manager
, &now
);
806 if (manager
->nscheduled
> 0) {
807 XTRACETIME2(isc_msgcat_get(isc_msgcat
,
812 result
= WAITUNTIL(&manager
->wakeup
, &manager
->lock
, &manager
->due
);
813 INSIST(result
== ISC_R_SUCCESS
||
814 result
== ISC_R_TIMEDOUT
);
816 XTRACETIME(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
817 ISC_MSG_WAIT
, "wait"), now
);
818 WAIT(&manager
->wakeup
, &manager
->lock
);
820 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
821 ISC_MSG_WAKEUP
, "wakeup"));
823 UNLOCK(&manager
->lock
);
829 return ((isc_threadresult_t
)0);
831 #endif /* USE_TIMER_THREAD */
834 sooner(void *v1
, void *v2
) {
835 isc__timer_t
*t1
, *t2
;
839 REQUIRE(VALID_TIMER(t1
));
840 REQUIRE(VALID_TIMER(t2
));
842 if (isc_time_compare(&t1
->due
, &t2
->due
) < 0)
848 set_index(void *what
, unsigned int index
) {
852 REQUIRE(VALID_TIMER(timer
));
854 timer
->index
= index
;
858 isc__timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
) {
859 isc__timermgr_t
*manager
;
863 * Create a timer manager.
866 REQUIRE(managerp
!= NULL
&& *managerp
== NULL
);
868 #ifdef USE_SHARED_MANAGER
869 if (timermgr
!= NULL
) {
871 *managerp
= (isc_timermgr_t
*)timermgr
;
872 return (ISC_R_SUCCESS
);
874 #endif /* USE_SHARED_MANAGER */
876 manager
= isc_mem_get(mctx
, sizeof(*manager
));
878 return (ISC_R_NOMEMORY
);
880 manager
->common
.impmagic
= TIMER_MANAGER_MAGIC
;
881 manager
->common
.magic
= ISCAPI_TIMERMGR_MAGIC
;
882 manager
->common
.methods
= (isc_timermgrmethods_t
*)&timermgrmethods
;
883 manager
->mctx
= NULL
;
884 manager
->done
= ISC_FALSE
;
885 INIT_LIST(manager
->timers
);
886 manager
->nscheduled
= 0;
887 isc_time_settoepoch(&manager
->due
);
888 manager
->heap
= NULL
;
889 result
= isc_heap_create(mctx
, sooner
, set_index
, 0, &manager
->heap
);
890 if (result
!= ISC_R_SUCCESS
) {
891 INSIST(result
== ISC_R_NOMEMORY
);
892 isc_mem_put(mctx
, manager
, sizeof(*manager
));
893 return (ISC_R_NOMEMORY
);
895 result
= isc_mutex_init(&manager
->lock
);
896 if (result
!= ISC_R_SUCCESS
) {
897 isc_heap_destroy(&manager
->heap
);
898 isc_mem_put(mctx
, manager
, sizeof(*manager
));
901 isc_mem_attach(mctx
, &manager
->mctx
);
902 #ifdef USE_TIMER_THREAD
903 if (isc_condition_init(&manager
->wakeup
) != ISC_R_SUCCESS
) {
904 isc_mem_detach(&manager
->mctx
);
905 DESTROYLOCK(&manager
->lock
);
906 isc_heap_destroy(&manager
->heap
);
907 isc_mem_put(mctx
, manager
, sizeof(*manager
));
908 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
909 "isc_condition_init() %s",
910 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
911 ISC_MSG_FAILED
, "failed"));
912 return (ISC_R_UNEXPECTED
);
914 if (isc_thread_create(run
, manager
, &manager
->thread
) !=
916 isc_mem_detach(&manager
->mctx
);
917 (void)isc_condition_destroy(&manager
->wakeup
);
918 DESTROYLOCK(&manager
->lock
);
919 isc_heap_destroy(&manager
->heap
);
920 isc_mem_put(mctx
, manager
, sizeof(*manager
));
921 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
922 "isc_thread_create() %s",
923 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
924 ISC_MSG_FAILED
, "failed"));
925 return (ISC_R_UNEXPECTED
);
928 #ifdef USE_SHARED_MANAGER
931 #endif /* USE_SHARED_MANAGER */
933 *managerp
= (isc_timermgr_t
*)manager
;
935 return (ISC_R_SUCCESS
);
939 isc_timermgr_poke(isc_timermgr_t
*manager0
) {
940 #ifdef USE_TIMER_THREAD
941 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
943 REQUIRE(VALID_MANAGER(manager
));
945 SIGNAL(&manager
->wakeup
);
952 isc__timermgr_destroy(isc_timermgr_t
**managerp
) {
953 isc__timermgr_t
*manager
;
957 * Destroy a timer manager.
960 REQUIRE(managerp
!= NULL
);
961 manager
= (isc__timermgr_t
*)*managerp
;
962 REQUIRE(VALID_MANAGER(manager
));
964 LOCK(&manager
->lock
);
966 #ifdef USE_SHARED_MANAGER
968 if (manager
->refs
> 0) {
969 UNLOCK(&manager
->lock
);
974 #endif /* USE_SHARED_MANAGER */
976 #ifndef USE_TIMER_THREAD
977 isc__timermgr_dispatch((isc_timermgr_t
*)manager
);
980 REQUIRE(EMPTY(manager
->timers
));
981 manager
->done
= ISC_TRUE
;
983 #ifdef USE_TIMER_THREAD
984 XTRACE(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_TIMER
,
985 ISC_MSG_SIGNALDESTROY
, "signal (destroy)"));
986 SIGNAL(&manager
->wakeup
);
987 #endif /* USE_TIMER_THREAD */
989 UNLOCK(&manager
->lock
);
991 #ifdef USE_TIMER_THREAD
993 * Wait for thread to exit.
995 if (isc_thread_join(manager
->thread
, NULL
) != ISC_R_SUCCESS
)
996 UNEXPECTED_ERROR(__FILE__
, __LINE__
,
997 "isc_thread_join() %s",
998 isc_msgcat_get(isc_msgcat
, ISC_MSGSET_GENERAL
,
999 ISC_MSG_FAILED
, "failed"));
1000 #endif /* USE_TIMER_THREAD */
1005 #ifdef USE_TIMER_THREAD
1006 (void)isc_condition_destroy(&manager
->wakeup
);
1007 #endif /* USE_TIMER_THREAD */
1008 DESTROYLOCK(&manager
->lock
);
1009 isc_heap_destroy(&manager
->heap
);
1010 manager
->common
.impmagic
= 0;
1011 manager
->common
.magic
= 0;
1012 mctx
= manager
->mctx
;
1013 isc_mem_put(mctx
, manager
, sizeof(*manager
));
1014 isc_mem_detach(&mctx
);
1018 #ifdef USE_SHARED_MANAGER
1023 #ifndef USE_TIMER_THREAD
1025 isc__timermgr_nextevent(isc_timermgr_t
*manager0
, isc_time_t
*when
) {
1026 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
1028 #ifdef USE_SHARED_MANAGER
1029 if (manager
== NULL
)
1032 if (manager
== NULL
|| manager
->nscheduled
== 0)
1033 return (ISC_R_NOTFOUND
);
1034 *when
= manager
->due
;
1035 return (ISC_R_SUCCESS
);
1039 isc__timermgr_dispatch(isc_timermgr_t
*manager0
) {
1040 isc__timermgr_t
*manager
= (isc__timermgr_t
*)manager0
;
1043 #ifdef USE_SHARED_MANAGER
1044 if (manager
== NULL
)
1047 if (manager
== NULL
)
1050 dispatch(manager
, &now
);
1052 #endif /* USE_TIMER_THREAD */
1055 isc__timer_register(void) {
1056 return (isc_timer_register(isc__timermgr_create
));
1059 static isc_mutex_t createlock
;
1060 static isc_once_t once
= ISC_ONCE_INIT
;
1061 static isc_timermgrcreatefunc_t timermgr_createfunc
= NULL
;
1065 RUNTIME_CHECK(isc_mutex_init(&createlock
) == ISC_R_SUCCESS
);
1069 isc_timer_register(isc_timermgrcreatefunc_t createfunc
) {
1070 isc_result_t result
= ISC_R_SUCCESS
;
1072 RUNTIME_CHECK(isc_once_do(&once
, initialize
) == ISC_R_SUCCESS
);
1075 if (timermgr_createfunc
== NULL
)
1076 timermgr_createfunc
= createfunc
;
1078 result
= ISC_R_EXISTS
;
1079 UNLOCK(&createlock
);
1085 isc_timermgr_createinctx(isc_mem_t
*mctx
, isc_appctx_t
*actx
,
1086 isc_timermgr_t
**managerp
)
1088 isc_result_t result
;
1092 REQUIRE(timermgr_createfunc
!= NULL
);
1093 result
= (*timermgr_createfunc
)(mctx
, managerp
);
1095 UNLOCK(&createlock
);
1097 if (result
== ISC_R_SUCCESS
)
1098 isc_appctx_settimermgr(actx
, *managerp
);
1104 isc_timermgr_create(isc_mem_t
*mctx
, isc_timermgr_t
**managerp
) {
1105 isc_result_t result
;
1108 return (isc__timermgr_create(mctx
, managerp
));
1112 REQUIRE(timermgr_createfunc
!= NULL
);
1113 result
= (*timermgr_createfunc
)(mctx
, managerp
);
1115 UNLOCK(&createlock
);
1121 isc_timermgr_destroy(isc_timermgr_t
**managerp
) {
1122 REQUIRE(*managerp
!= NULL
&& ISCAPI_TIMERMGR_VALID(*managerp
));
1125 isc__timermgr_destroy(managerp
);
1127 (*managerp
)->methods
->destroy(managerp
);
1129 ENSURE(*managerp
== NULL
);
1133 isc_timer_create(isc_timermgr_t
*manager
, isc_timertype_t type
,
1134 const isc_time_t
*expires
, const isc_interval_t
*interval
,
1135 isc_task_t
*task
, isc_taskaction_t action
, void *arg
,
1136 isc_timer_t
**timerp
)
1138 REQUIRE(ISCAPI_TIMERMGR_VALID(manager
));
1141 return (isc__timer_create(manager
, type
, expires
, interval
,
1142 task
, action
, arg
, timerp
));
1144 return (manager
->methods
->timercreate(manager
, type
, expires
,
1145 interval
, task
, action
, arg
,
1150 isc_timer_attach(isc_timer_t
*timer
, isc_timer_t
**timerp
) {
1151 REQUIRE(ISCAPI_TIMER_VALID(timer
));
1152 REQUIRE(timerp
!= NULL
&& *timerp
== NULL
);
1155 isc__timer_attach(timer
, timerp
);
1157 timer
->methods
->attach(timer
, timerp
);
1159 ENSURE(*timerp
== timer
);
1163 isc_timer_detach(isc_timer_t
**timerp
) {
1164 REQUIRE(timerp
!= NULL
&& ISCAPI_TIMER_VALID(*timerp
));
1167 isc__timer_detach(timerp
);
1169 (*timerp
)->methods
->detach(timerp
);
1171 ENSURE(*timerp
== NULL
);
1175 isc_timer_reset(isc_timer_t
*timer
, isc_timertype_t type
,
1176 const isc_time_t
*expires
, const isc_interval_t
*interval
,
1177 isc_boolean_t purge
)
1179 REQUIRE(ISCAPI_TIMER_VALID(timer
));
1182 return (isc__timer_reset(timer
, type
, expires
,
1185 return (timer
->methods
->reset(timer
, type
, expires
, interval
, purge
));
1189 isc_timer_touch(isc_timer_t
*timer
) {
1190 REQUIRE(ISCAPI_TIMER_VALID(timer
));
1193 return (isc__timer_touch(timer
));
1195 return (timer
->methods
->touch(timer
));