Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / ntp / dist / lib / isc / timer.c
blob19ac9b1ba27f8e708da6747614d610dfc626c3b2
1 /* $NetBSD$ */
3 /*
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.84.58.4 2009/01/23 23:47:21 tbox Exp */
22 /*! \file */
24 #include <config.h>
26 #include <isc/condition.h>
27 #include <isc/heap.h>
28 #include <isc/log.h>
29 #include <isc/magic.h>
30 #include <isc/mem.h>
31 #include <isc/msgs.h>
32 #include <isc/platform.h>
33 #include <isc/task.h>
34 #include <isc/thread.h>
35 #include <isc/time.h>
36 #include <isc/timer.h>
37 #include <isc/util.h>
39 #ifndef ISC_PLATFORM_USETHREADS
40 #include "timer_p.h"
41 #endif /* ISC_PLATFORM_USETHREADS */
43 #ifdef ISC_TIMER_TRACE
44 #define XTRACE(s) fprintf(stderr, "%s\n", (s))
45 #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
46 #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \
47 (d).seconds, (d).nanoseconds)
48 #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
49 (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
50 #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
51 (d).seconds, (d).nanoseconds)
52 #else
53 #define XTRACE(s)
54 #define XTRACEID(s, t)
55 #define XTRACETIME(s, d)
56 #define XTRACETIME2(s, d, n)
57 #define XTRACETIMER(s, t, d)
58 #endif /* ISC_TIMER_TRACE */
60 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
61 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
63 struct isc_timer {
64 /*! Not locked. */
65 unsigned int magic;
66 isc_timermgr_t * manager;
67 isc_mutex_t lock;
68 /*! Locked by timer lock. */
69 unsigned int references;
70 isc_time_t idle;
71 /*! Locked by manager lock. */
72 isc_timertype_t type;
73 isc_time_t expires;
74 isc_interval_t interval;
75 isc_task_t * task;
76 isc_taskaction_t action;
77 void * arg;
78 unsigned int index;
79 isc_time_t due;
80 LINK(isc_timer_t) link;
83 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
84 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
86 struct isc_timermgr {
87 /* Not locked. */
88 unsigned int magic;
89 isc_mem_t * mctx;
90 isc_mutex_t lock;
91 /* Locked by manager lock. */
92 isc_boolean_t done;
93 LIST(isc_timer_t) timers;
94 unsigned int nscheduled;
95 isc_time_t due;
96 #ifdef ISC_PLATFORM_USETHREADS
97 isc_condition_t wakeup;
98 isc_thread_t thread;
99 #else /* ISC_PLATFORM_USETHREADS */
100 unsigned int refs;
101 #endif /* ISC_PLATFORM_USETHREADS */
102 isc_heap_t * heap;
105 #ifndef ISC_PLATFORM_USETHREADS
107 * If threads are not in use, there can be only one.
109 static isc_timermgr_t *timermgr = NULL;
110 #endif /* ISC_PLATFORM_USETHREADS */
112 static inline isc_result_t
113 schedule(isc_timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
114 isc_result_t result;
115 isc_timermgr_t *manager;
116 isc_time_t due;
117 int cmp;
118 #ifdef ISC_PLATFORM_USETHREADS
119 isc_boolean_t timedwait;
120 #endif
123 * Note: the caller must ensure locking.
126 REQUIRE(timer->type != isc_timertype_inactive);
128 #ifndef ISC_PLATFORM_USETHREADS
129 UNUSED(signal_ok);
130 #endif /* ISC_PLATFORM_USETHREADS */
132 manager = timer->manager;
134 #ifdef ISC_PLATFORM_USETHREADS
136 * If the manager was timed wait, we may need to signal the
137 * manager to force a wakeup.
139 timedwait = ISC_TF(manager->nscheduled > 0 &&
140 isc_time_seconds(&manager->due) != 0);
141 #endif
144 * Compute the new due time.
146 if (timer->type != isc_timertype_once) {
147 result = isc_time_add(now, &timer->interval, &due);
148 if (result != ISC_R_SUCCESS)
149 return (result);
150 if (timer->type == isc_timertype_limited &&
151 isc_time_compare(&timer->expires, &due) < 0)
152 due = timer->expires;
153 } else {
154 if (isc_time_isepoch(&timer->idle))
155 due = timer->expires;
156 else if (isc_time_isepoch(&timer->expires))
157 due = timer->idle;
158 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
159 due = timer->idle;
160 else
161 due = timer->expires;
165 * Schedule the timer.
168 if (timer->index > 0) {
170 * Already scheduled.
172 cmp = isc_time_compare(&due, &timer->due);
173 timer->due = due;
174 switch (cmp) {
175 case -1:
176 isc_heap_increased(manager->heap, timer->index);
177 break;
178 case 1:
179 isc_heap_decreased(manager->heap, timer->index);
180 break;
181 case 0:
182 /* Nothing to do. */
183 break;
185 } else {
186 timer->due = due;
187 result = isc_heap_insert(manager->heap, timer);
188 if (result != ISC_R_SUCCESS) {
189 INSIST(result == ISC_R_NOMEMORY);
190 return (ISC_R_NOMEMORY);
192 manager->nscheduled++;
195 XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
196 ISC_MSG_SCHEDULE, "schedule"), timer, due);
199 * If this timer is at the head of the queue, we need to ensure
200 * that we won't miss it if it has a more recent due time than
201 * the current "next" timer. We do this either by waking up the
202 * run thread, or explicitly setting the value in the manager.
204 #ifdef ISC_PLATFORM_USETHREADS
207 * This is a temporary (probably) hack to fix a bug on tru64 5.1
208 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
209 * return when the time expires, so here, we check to see if
210 * we're 15 seconds or more behind, and if we are, we signal
211 * the dispatcher. This isn't such a bad idea as a general purpose
212 * watchdog, so perhaps we should just leave it in here.
214 if (signal_ok && timedwait) {
215 isc_interval_t fifteen;
216 isc_time_t then;
218 isc_interval_set(&fifteen, 15, 0);
219 result = isc_time_add(&manager->due, &fifteen, &then);
221 if (result == ISC_R_SUCCESS &&
222 isc_time_compare(&then, now) < 0) {
223 SIGNAL(&manager->wakeup);
224 signal_ok = ISC_FALSE;
225 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
226 ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
227 "*** POKED TIMER ***");
231 if (timer->index == 1 && signal_ok) {
232 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
233 ISC_MSG_SIGNALSCHED,
234 "signal (schedule)"));
235 SIGNAL(&manager->wakeup);
237 #else /* ISC_PLATFORM_USETHREADS */
238 if (timer->index == 1 &&
239 isc_time_compare(&timer->due, &manager->due) < 0)
240 manager->due = timer->due;
241 #endif /* ISC_PLATFORM_USETHREADS */
243 return (ISC_R_SUCCESS);
246 static inline void
247 deschedule(isc_timer_t *timer) {
248 isc_boolean_t need_wakeup = ISC_FALSE;
249 isc_timermgr_t *manager;
252 * The caller must ensure locking.
255 manager = timer->manager;
256 if (timer->index > 0) {
257 if (timer->index == 1)
258 need_wakeup = ISC_TRUE;
259 isc_heap_delete(manager->heap, timer->index);
260 timer->index = 0;
261 INSIST(manager->nscheduled > 0);
262 manager->nscheduled--;
263 #ifdef ISC_PLATFORM_USETHREADS
264 if (need_wakeup) {
265 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
266 ISC_MSG_SIGNALDESCHED,
267 "signal (deschedule)"));
268 SIGNAL(&manager->wakeup);
270 #endif /* ISC_PLATFORM_USETHREADS */
274 static void
275 destroy(isc_timer_t *timer) {
276 isc_timermgr_t *manager = timer->manager;
279 * The caller must ensure it is safe to destroy the timer.
282 LOCK(&manager->lock);
284 (void)isc_task_purgerange(timer->task,
285 timer,
286 ISC_TIMEREVENT_FIRSTEVENT,
287 ISC_TIMEREVENT_LASTEVENT,
288 NULL);
289 deschedule(timer);
290 UNLINK(manager->timers, timer, link);
292 UNLOCK(&manager->lock);
294 isc_task_detach(&timer->task);
295 DESTROYLOCK(&timer->lock);
296 timer->magic = 0;
297 isc_mem_put(manager->mctx, timer, sizeof(*timer));
300 isc_result_t
301 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
302 isc_time_t *expires, isc_interval_t *interval,
303 isc_task_t *task, isc_taskaction_t action, const void *arg,
304 isc_timer_t **timerp)
306 isc_timer_t *timer;
307 isc_result_t result;
308 isc_time_t now;
311 * Create a new 'type' timer managed by 'manager'. The timers
312 * parameters are specified by 'expires' and 'interval'. Events
313 * will be posted to 'task' and when dispatched 'action' will be
314 * called with 'arg' as the arg value. The new timer is returned
315 * in 'timerp'.
318 REQUIRE(VALID_MANAGER(manager));
319 REQUIRE(task != NULL);
320 REQUIRE(action != NULL);
321 if (expires == NULL)
322 expires = isc_time_epoch;
323 if (interval == NULL)
324 interval = isc_interval_zero;
325 REQUIRE(type == isc_timertype_inactive ||
326 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
327 REQUIRE(timerp != NULL && *timerp == NULL);
328 REQUIRE(type != isc_timertype_limited ||
329 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
332 * Get current time.
334 if (type != isc_timertype_inactive) {
335 TIME_NOW(&now);
336 } else {
338 * We don't have to do this, but it keeps the compiler from
339 * complaining about "now" possibly being used without being
340 * set, even though it will never actually happen.
342 isc_time_settoepoch(&now);
346 timer = isc_mem_get(manager->mctx, sizeof(*timer));
347 if (timer == NULL)
348 return (ISC_R_NOMEMORY);
350 timer->manager = manager;
351 timer->references = 1;
353 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
354 result = isc_time_add(&now, interval, &timer->idle);
355 if (result != ISC_R_SUCCESS) {
356 isc_mem_put(manager->mctx, timer, sizeof(*timer));
357 return (result);
359 } else
360 isc_time_settoepoch(&timer->idle);
362 timer->type = type;
363 timer->expires = *expires;
364 timer->interval = *interval;
365 timer->task = NULL;
366 isc_task_attach(task, &timer->task);
367 timer->action = action;
369 * Removing the const attribute from "arg" is the best of two
370 * evils here. If the timer->arg member is made const, then
371 * it affects a great many recipients of the timer event
372 * which did not pass in an "arg" that was truly const.
373 * Changing isc_timer_create() to not have "arg" prototyped as const,
374 * though, can cause compilers warnings for calls that *do*
375 * have a truly const arg. The caller will have to carefully
376 * keep track of whether arg started as a true const.
378 DE_CONST(arg, timer->arg);
379 timer->index = 0;
380 result = isc_mutex_init(&timer->lock);
381 if (result != ISC_R_SUCCESS) {
382 isc_task_detach(&timer->task);
383 isc_mem_put(manager->mctx, timer, sizeof(*timer));
384 return (result);
386 ISC_LINK_INIT(timer, link);
387 timer->magic = TIMER_MAGIC;
389 LOCK(&manager->lock);
392 * Note we don't have to lock the timer like we normally would because
393 * there are no external references to it yet.
396 if (type != isc_timertype_inactive)
397 result = schedule(timer, &now, ISC_TRUE);
398 else
399 result = ISC_R_SUCCESS;
400 if (result == ISC_R_SUCCESS)
401 APPEND(manager->timers, timer, link);
403 UNLOCK(&manager->lock);
405 if (result != ISC_R_SUCCESS) {
406 timer->magic = 0;
407 DESTROYLOCK(&timer->lock);
408 isc_task_detach(&timer->task);
409 isc_mem_put(manager->mctx, timer, sizeof(*timer));
410 return (result);
413 *timerp = timer;
415 return (ISC_R_SUCCESS);
418 isc_result_t
419 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
420 isc_time_t *expires, isc_interval_t *interval,
421 isc_boolean_t purge)
423 isc_time_t now;
424 isc_timermgr_t *manager;
425 isc_result_t result;
428 * Change the timer's type, expires, and interval values to the given
429 * values. If 'purge' is ISC_TRUE, any pending events from this timer
430 * are purged from its task's event queue.
433 REQUIRE(VALID_TIMER(timer));
434 manager = timer->manager;
435 REQUIRE(VALID_MANAGER(manager));
436 if (expires == NULL)
437 expires = isc_time_epoch;
438 if (interval == NULL)
439 interval = isc_interval_zero;
440 REQUIRE(type == isc_timertype_inactive ||
441 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
442 REQUIRE(type != isc_timertype_limited ||
443 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
446 * Get current time.
448 if (type != isc_timertype_inactive) {
449 TIME_NOW(&now);
450 } else {
452 * We don't have to do this, but it keeps the compiler from
453 * complaining about "now" possibly being used without being
454 * set, even though it will never actually happen.
456 isc_time_settoepoch(&now);
459 manager = timer->manager;
461 LOCK(&manager->lock);
462 LOCK(&timer->lock);
464 if (purge)
465 (void)isc_task_purgerange(timer->task,
466 timer,
467 ISC_TIMEREVENT_FIRSTEVENT,
468 ISC_TIMEREVENT_LASTEVENT,
469 NULL);
470 timer->type = type;
471 timer->expires = *expires;
472 timer->interval = *interval;
473 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
474 result = isc_time_add(&now, interval, &timer->idle);
475 } else {
476 isc_time_settoepoch(&timer->idle);
477 result = ISC_R_SUCCESS;
480 if (result == ISC_R_SUCCESS) {
481 if (type == isc_timertype_inactive) {
482 deschedule(timer);
483 result = ISC_R_SUCCESS;
484 } else
485 result = schedule(timer, &now, ISC_TRUE);
488 UNLOCK(&timer->lock);
489 UNLOCK(&manager->lock);
491 return (result);
494 isc_timertype_t
495 isc_timer_gettype(isc_timer_t *timer) {
496 isc_timertype_t t;
498 REQUIRE(VALID_TIMER(timer));
500 LOCK(&timer->lock);
501 t = timer->type;
502 UNLOCK(&timer->lock);
504 return (t);
507 isc_result_t
508 isc_timer_touch(isc_timer_t *timer) {
509 isc_result_t result;
510 isc_time_t now;
513 * Set the last-touched time of 'timer' to the current time.
516 REQUIRE(VALID_TIMER(timer));
518 LOCK(&timer->lock);
521 * We'd like to
523 * REQUIRE(timer->type == isc_timertype_once);
525 * but we cannot without locking the manager lock too, which we
526 * don't want to do.
529 TIME_NOW(&now);
530 result = isc_time_add(&now, &timer->interval, &timer->idle);
532 UNLOCK(&timer->lock);
534 return (result);
537 void
538 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
540 * Attach *timerp to timer.
543 REQUIRE(VALID_TIMER(timer));
544 REQUIRE(timerp != NULL && *timerp == NULL);
546 LOCK(&timer->lock);
547 timer->references++;
548 UNLOCK(&timer->lock);
550 *timerp = timer;
553 void
554 isc_timer_detach(isc_timer_t **timerp) {
555 isc_timer_t *timer;
556 isc_boolean_t free_timer = ISC_FALSE;
559 * Detach *timerp from its timer.
562 REQUIRE(timerp != NULL);
563 timer = *timerp;
564 REQUIRE(VALID_TIMER(timer));
566 LOCK(&timer->lock);
567 REQUIRE(timer->references > 0);
568 timer->references--;
569 if (timer->references == 0)
570 free_timer = ISC_TRUE;
571 UNLOCK(&timer->lock);
573 if (free_timer)
574 destroy(timer);
576 *timerp = NULL;
579 static void
580 dispatch(isc_timermgr_t *manager, isc_time_t *now) {
581 isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
582 isc_timerevent_t *event;
583 isc_eventtype_t type = 0;
584 isc_timer_t *timer;
585 isc_result_t result;
586 isc_boolean_t idle;
589 * The caller must be holding the manager lock.
592 while (manager->nscheduled > 0 && !done) {
593 timer = isc_heap_element(manager->heap, 1);
594 INSIST(timer->type != isc_timertype_inactive);
595 if (isc_time_compare(now, &timer->due) >= 0) {
596 if (timer->type == isc_timertype_ticker) {
597 type = ISC_TIMEREVENT_TICK;
598 post_event = ISC_TRUE;
599 need_schedule = ISC_TRUE;
600 } else if (timer->type == isc_timertype_limited) {
601 int cmp;
602 cmp = isc_time_compare(now, &timer->expires);
603 if (cmp >= 0) {
604 type = ISC_TIMEREVENT_LIFE;
605 post_event = ISC_TRUE;
606 need_schedule = ISC_FALSE;
607 } else {
608 type = ISC_TIMEREVENT_TICK;
609 post_event = ISC_TRUE;
610 need_schedule = ISC_TRUE;
612 } else if (!isc_time_isepoch(&timer->expires) &&
613 isc_time_compare(now,
614 &timer->expires) >= 0) {
615 type = ISC_TIMEREVENT_LIFE;
616 post_event = ISC_TRUE;
617 need_schedule = ISC_FALSE;
618 } else {
619 idle = ISC_FALSE;
621 LOCK(&timer->lock);
622 if (!isc_time_isepoch(&timer->idle) &&
623 isc_time_compare(now,
624 &timer->idle) >= 0) {
625 idle = ISC_TRUE;
627 UNLOCK(&timer->lock);
628 if (idle) {
629 type = ISC_TIMEREVENT_IDLE;
630 post_event = ISC_TRUE;
631 need_schedule = ISC_FALSE;
632 } else {
634 * Idle timer has been touched;
635 * reschedule.
637 XTRACEID(isc_msgcat_get(isc_msgcat,
638 ISC_MSGSET_TIMER,
639 ISC_MSG_IDLERESCHED,
640 "idle reschedule"),
641 timer);
642 post_event = ISC_FALSE;
643 need_schedule = ISC_TRUE;
647 if (post_event) {
648 XTRACEID(isc_msgcat_get(isc_msgcat,
649 ISC_MSGSET_TIMER,
650 ISC_MSG_POSTING,
651 "posting"), timer);
653 * XXX We could preallocate this event.
655 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
656 timer,
657 type,
658 timer->action,
659 timer->arg,
660 sizeof(*event));
662 if (event != NULL) {
663 event->due = timer->due;
664 isc_task_send(timer->task,
665 ISC_EVENT_PTR(&event));
666 } else
667 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
668 isc_msgcat_get(isc_msgcat,
669 ISC_MSGSET_TIMER,
670 ISC_MSG_EVENTNOTALLOC,
671 "couldn't "
672 "allocate event"));
675 timer->index = 0;
676 isc_heap_delete(manager->heap, 1);
677 manager->nscheduled--;
679 if (need_schedule) {
680 result = schedule(timer, now, ISC_FALSE);
681 if (result != ISC_R_SUCCESS)
682 UNEXPECTED_ERROR(__FILE__, __LINE__,
683 "%s: %u",
684 isc_msgcat_get(isc_msgcat,
685 ISC_MSGSET_TIMER,
686 ISC_MSG_SCHEDFAIL,
687 "couldn't schedule "
688 "timer"),
689 result);
691 } else {
692 manager->due = timer->due;
693 done = ISC_TRUE;
698 #ifdef ISC_PLATFORM_USETHREADS
699 static isc_threadresult_t
700 #ifdef _WIN32 /* XXXDCL */
701 WINAPI
702 #endif
703 run(void *uap) {
704 isc_timermgr_t *manager = uap;
705 isc_time_t now;
706 isc_result_t result;
708 LOCK(&manager->lock);
709 while (!manager->done) {
710 TIME_NOW(&now);
712 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
713 ISC_MSG_RUNNING,
714 "running"), now);
716 dispatch(manager, &now);
718 if (manager->nscheduled > 0) {
719 XTRACETIME2(isc_msgcat_get(isc_msgcat,
720 ISC_MSGSET_GENERAL,
721 ISC_MSG_WAITUNTIL,
722 "waituntil"),
723 manager->due, now);
724 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
725 INSIST(result == ISC_R_SUCCESS ||
726 result == ISC_R_TIMEDOUT);
727 } else {
728 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
729 ISC_MSG_WAIT, "wait"), now);
730 WAIT(&manager->wakeup, &manager->lock);
732 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
733 ISC_MSG_WAKEUP, "wakeup"));
735 UNLOCK(&manager->lock);
737 return ((isc_threadresult_t)0);
739 #endif /* ISC_PLATFORM_USETHREADS */
741 static isc_boolean_t
742 sooner(void *v1, void *v2) {
743 isc_timer_t *t1, *t2;
745 t1 = v1;
746 t2 = v2;
747 REQUIRE(VALID_TIMER(t1));
748 REQUIRE(VALID_TIMER(t2));
750 if (isc_time_compare(&t1->due, &t2->due) < 0)
751 return (ISC_TRUE);
752 return (ISC_FALSE);
755 static void
756 set_index(void *what, unsigned int index) {
757 isc_timer_t *timer;
759 timer = what;
760 REQUIRE(VALID_TIMER(timer));
762 timer->index = index;
765 isc_result_t
766 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
767 isc_timermgr_t *manager;
768 isc_result_t result;
771 * Create a timer manager.
774 REQUIRE(managerp != NULL && *managerp == NULL);
776 #ifndef ISC_PLATFORM_USETHREADS
777 if (timermgr != NULL) {
778 timermgr->refs++;
779 *managerp = timermgr;
780 return (ISC_R_SUCCESS);
782 #endif /* ISC_PLATFORM_USETHREADS */
784 manager = isc_mem_get(mctx, sizeof(*manager));
785 if (manager == NULL)
786 return (ISC_R_NOMEMORY);
788 manager->magic = TIMER_MANAGER_MAGIC;
789 manager->mctx = NULL;
790 manager->done = ISC_FALSE;
791 INIT_LIST(manager->timers);
792 manager->nscheduled = 0;
793 isc_time_settoepoch(&manager->due);
794 manager->heap = NULL;
795 result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
796 if (result != ISC_R_SUCCESS) {
797 INSIST(result == ISC_R_NOMEMORY);
798 isc_mem_put(mctx, manager, sizeof(*manager));
799 return (ISC_R_NOMEMORY);
801 result = isc_mutex_init(&manager->lock);
802 if (result != ISC_R_SUCCESS) {
803 isc_heap_destroy(&manager->heap);
804 isc_mem_put(mctx, manager, sizeof(*manager));
805 return (result);
807 isc_mem_attach(mctx, &manager->mctx);
808 #ifdef ISC_PLATFORM_USETHREADS
809 if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
810 isc_mem_detach(&manager->mctx);
811 DESTROYLOCK(&manager->lock);
812 isc_heap_destroy(&manager->heap);
813 isc_mem_put(mctx, manager, sizeof(*manager));
814 UNEXPECTED_ERROR(__FILE__, __LINE__,
815 "isc_condition_init() %s",
816 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
817 ISC_MSG_FAILED, "failed"));
818 return (ISC_R_UNEXPECTED);
820 if (isc_thread_create(run, manager, &manager->thread) !=
821 ISC_R_SUCCESS) {
822 isc_mem_detach(&manager->mctx);
823 (void)isc_condition_destroy(&manager->wakeup);
824 DESTROYLOCK(&manager->lock);
825 isc_heap_destroy(&manager->heap);
826 isc_mem_put(mctx, manager, sizeof(*manager));
827 UNEXPECTED_ERROR(__FILE__, __LINE__,
828 "isc_thread_create() %s",
829 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
830 ISC_MSG_FAILED, "failed"));
831 return (ISC_R_UNEXPECTED);
833 #else /* ISC_PLATFORM_USETHREADS */
834 manager->refs = 1;
835 timermgr = manager;
836 #endif /* ISC_PLATFORM_USETHREADS */
838 *managerp = manager;
840 return (ISC_R_SUCCESS);
843 void
844 isc_timermgr_poke(isc_timermgr_t *manager) {
845 #ifdef ISC_PLATFORM_USETHREADS
846 REQUIRE(VALID_MANAGER(manager));
848 SIGNAL(&manager->wakeup);
849 #else
850 UNUSED(manager);
851 #endif
854 void
855 isc_timermgr_destroy(isc_timermgr_t **managerp) {
856 isc_timermgr_t *manager;
857 isc_mem_t *mctx;
860 * Destroy a timer manager.
863 REQUIRE(managerp != NULL);
864 manager = *managerp;
865 REQUIRE(VALID_MANAGER(manager));
867 LOCK(&manager->lock);
869 #ifndef ISC_PLATFORM_USETHREADS
870 if (manager->refs > 1) {
871 manager->refs--;
872 UNLOCK(&manager->lock);
873 *managerp = NULL;
874 return;
877 isc__timermgr_dispatch();
878 #endif /* ISC_PLATFORM_USETHREADS */
880 REQUIRE(EMPTY(manager->timers));
881 manager->done = ISC_TRUE;
883 #ifdef ISC_PLATFORM_USETHREADS
884 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
885 ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
886 SIGNAL(&manager->wakeup);
887 #endif /* ISC_PLATFORM_USETHREADS */
889 UNLOCK(&manager->lock);
891 #ifdef ISC_PLATFORM_USETHREADS
893 * Wait for thread to exit.
895 if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
896 UNEXPECTED_ERROR(__FILE__, __LINE__,
897 "isc_thread_join() %s",
898 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
899 ISC_MSG_FAILED, "failed"));
900 #endif /* ISC_PLATFORM_USETHREADS */
903 * Clean up.
905 #ifdef ISC_PLATFORM_USETHREADS
906 (void)isc_condition_destroy(&manager->wakeup);
907 #endif /* ISC_PLATFORM_USETHREADS */
908 DESTROYLOCK(&manager->lock);
909 isc_heap_destroy(&manager->heap);
910 manager->magic = 0;
911 mctx = manager->mctx;
912 isc_mem_put(mctx, manager, sizeof(*manager));
913 isc_mem_detach(&mctx);
915 *managerp = NULL;
918 #ifndef ISC_PLATFORM_USETHREADS
919 isc_result_t
920 isc__timermgr_nextevent(isc_time_t *when) {
921 if (timermgr == NULL || timermgr->nscheduled == 0)
922 return (ISC_R_NOTFOUND);
923 *when = timermgr->due;
924 return (ISC_R_SUCCESS);
927 void
928 isc__timermgr_dispatch(void) {
929 isc_time_t now;
930 if (timermgr == NULL)
931 return;
932 TIME_NOW(&now);
933 dispatch(timermgr, &now);
935 #endif /* ISC_PLATFORM_USETHREADS */