No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / isc / timer.c
blob2b148c7bd74ba98c831450b6b32f2d5dcf1c080e
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.95 2009/10/05 17:30:49 fdupont 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 #ifdef OPENSSL_LEAKS
40 #include <openssl/err.h>
41 #endif
43 /* See task.c about the following definition: */
44 #ifdef BIND9
45 #ifdef ISC_PLATFORM_USETHREADS
46 #define USE_TIMER_THREAD
47 #else
48 #define USE_SHARED_MANAGER
49 #endif /* ISC_PLATFORM_USETHREADS */
50 #endif /* BIND9 */
52 #ifndef USE_TIMER_THREAD
53 #include "timer_p.h"
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)
65 #else
66 #define XTRACE(s)
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;
79 struct isc__timer {
80 /*! Not locked. */
81 isc_timer_t common;
82 isc__timermgr_t * manager;
83 isc_mutex_t lock;
84 /*! Locked by timer lock. */
85 unsigned int references;
86 isc_time_t idle;
87 /*! Locked by manager lock. */
88 isc_timertype_t type;
89 isc_time_t expires;
90 isc_interval_t interval;
91 isc_task_t * task;
92 isc_taskaction_t action;
93 void * arg;
94 unsigned int index;
95 isc_time_t due;
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 {
103 /* Not locked. */
104 isc_timermgr_t common;
105 isc_mem_t * mctx;
106 isc_mutex_t lock;
107 /* Locked by manager lock. */
108 isc_boolean_t done;
109 LIST(isc__timer_t) timers;
110 unsigned int nscheduled;
111 isc_time_t due;
112 #ifdef USE_TIMER_THREAD
113 isc_condition_t wakeup;
114 isc_thread_t thread;
115 #endif /* USE_TIMER_THREAD */
116 #ifdef USE_SHARED_MANAGER
117 unsigned int refs;
118 #endif /* USE_SHARED_MANAGER */
119 isc_heap_t * heap;
123 * The followings can be either static or public, depending on build
124 * environment.
127 #ifdef BIND9
128 #define ISC_TIMERFUNC_SCOPE
129 #else
130 #define ISC_TIMERFUNC_SCOPE static
131 #endif
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.
163 #ifndef BIND9
164 void *gettype;
165 #endif
166 } timermethods = {
168 isc__timer_attach,
169 isc__timer_detach,
170 isc__timer_reset,
171 isc__timer_touch
173 #ifndef BIND9
175 (void *)isc__timer_gettype
176 #endif
179 static struct isc__timermgrmethods {
180 isc_timermgrmethods_t methods;
181 #ifndef BIND9
182 void *poke; /* see above */
183 #endif
184 } timermgrmethods = {
186 isc__timermgr_destroy,
187 isc__timer_create
189 #ifndef BIND9
191 (void *)isc__timermgr_poke
192 #endif
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) {
204 isc_result_t result;
205 isc__timermgr_t *manager;
206 isc_time_t due;
207 int cmp;
208 #ifdef USE_TIMER_THREAD
209 isc_boolean_t timedwait;
210 #endif
213 * Note: the caller must ensure locking.
216 REQUIRE(timer->type != isc_timertype_inactive);
218 #ifndef USE_TIMER_THREAD
219 UNUSED(signal_ok);
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);
231 #endif
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)
239 return (result);
240 if (timer->type == isc_timertype_limited &&
241 isc_time_compare(&timer->expires, &due) < 0)
242 due = timer->expires;
243 } else {
244 if (isc_time_isepoch(&timer->idle))
245 due = timer->expires;
246 else if (isc_time_isepoch(&timer->expires))
247 due = timer->idle;
248 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
249 due = timer->idle;
250 else
251 due = timer->expires;
255 * Schedule the timer.
258 if (timer->index > 0) {
260 * Already scheduled.
262 cmp = isc_time_compare(&due, &timer->due);
263 timer->due = due;
264 switch (cmp) {
265 case -1:
266 isc_heap_increased(manager->heap, timer->index);
267 break;
268 case 1:
269 isc_heap_decreased(manager->heap, timer->index);
270 break;
271 case 0:
272 /* Nothing to do. */
273 break;
275 } else {
276 timer->due = due;
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;
306 isc_time_t then;
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,
323 ISC_MSG_SIGNALSCHED,
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);
336 static inline void
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);
350 timer->index = 0;
351 INSIST(manager->nscheduled > 0);
352 manager->nscheduled--;
353 #ifdef USE_TIMER_THREAD
354 if (need_wakeup) {
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 */
364 static void
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,
375 timer,
376 ISC_TIMEREVENT_FIRSTEVENT,
377 ISC_TIMEREVENT_LASTEVENT,
378 NULL);
379 deschedule(timer);
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;
398 isc__timer_t *timer;
399 isc_result_t result;
400 isc_time_t now;
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
407 * in 'timerp'.
410 REQUIRE(VALID_MANAGER(manager));
411 REQUIRE(task != NULL);
412 REQUIRE(action != NULL);
413 if (expires == 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)));
424 * Get current time.
426 if (type != isc_timertype_inactive) {
427 TIME_NOW(&now);
428 } else {
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));
439 if (timer == NULL)
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));
449 return (result);
451 } else
452 isc_time_settoepoch(&timer->idle);
454 timer->type = type;
455 timer->expires = *expires;
456 timer->interval = *interval;
457 timer->task = NULL;
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);
471 timer->index = 0;
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));
476 return (result);
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);
492 else
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));
505 return (result);
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,
516 isc_boolean_t purge)
518 isc__timer_t *timer = (isc__timer_t *)timer0;
519 isc_time_t now;
520 isc__timermgr_t *manager;
521 isc_result_t result;
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));
532 if (expires == NULL)
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)));
542 * Get current time.
544 if (type != isc_timertype_inactive) {
545 TIME_NOW(&now);
546 } else {
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);
558 LOCK(&timer->lock);
560 if (purge)
561 (void)isc_task_purgerange(timer->task,
562 timer,
563 ISC_TIMEREVENT_FIRSTEVENT,
564 ISC_TIMEREVENT_LASTEVENT,
565 NULL);
566 timer->type = type;
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);
571 } else {
572 isc_time_settoepoch(&timer->idle);
573 result = ISC_R_SUCCESS;
576 if (result == ISC_R_SUCCESS) {
577 if (type == isc_timertype_inactive) {
578 deschedule(timer);
579 result = ISC_R_SUCCESS;
580 } else
581 result = schedule(timer, &now, ISC_TRUE);
584 UNLOCK(&timer->lock);
585 UNLOCK(&manager->lock);
587 return (result);
590 ISC_TIMERFUNC_SCOPE isc_timertype_t
591 isc__timer_gettype(isc_timer_t *timer0) {
592 isc__timer_t *timer = (isc__timer_t *)timer0;
593 isc_timertype_t t;
595 REQUIRE(VALID_TIMER(timer));
597 LOCK(&timer->lock);
598 t = timer->type;
599 UNLOCK(&timer->lock);
601 return (t);
604 ISC_TIMERFUNC_SCOPE isc_result_t
605 isc__timer_touch(isc_timer_t *timer0) {
606 isc__timer_t *timer = (isc__timer_t *)timer0;
607 isc_result_t result;
608 isc_time_t now;
611 * Set the last-touched time of 'timer' to the current time.
614 REQUIRE(VALID_TIMER(timer));
616 LOCK(&timer->lock);
619 * We'd like to
621 * REQUIRE(timer->type == isc_timertype_once);
623 * but we cannot without locking the manager lock too, which we
624 * don't want to do.
627 TIME_NOW(&now);
628 result = isc_time_add(&now, &timer->interval, &timer->idle);
630 UNLOCK(&timer->lock);
632 return (result);
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);
646 LOCK(&timer->lock);
647 timer->references++;
648 UNLOCK(&timer->lock);
650 *timerp = (isc_timer_t *)timer;
653 ISC_TIMERFUNC_SCOPE void
654 isc__timer_detach(isc_timer_t **timerp) {
655 isc__timer_t *timer;
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));
666 LOCK(&timer->lock);
667 REQUIRE(timer->references > 0);
668 timer->references--;
669 if (timer->references == 0)
670 free_timer = ISC_TRUE;
671 UNLOCK(&timer->lock);
673 if (free_timer)
674 destroy(timer);
676 *timerp = NULL;
679 static void
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;
684 isc__timer_t *timer;
685 isc_result_t result;
686 isc_boolean_t idle;
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) {
701 int cmp;
702 cmp = isc_time_compare(now, &timer->expires);
703 if (cmp >= 0) {
704 type = ISC_TIMEREVENT_LIFE;
705 post_event = ISC_TRUE;
706 need_schedule = ISC_FALSE;
707 } else {
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;
718 } else {
719 idle = ISC_FALSE;
721 LOCK(&timer->lock);
722 if (!isc_time_isepoch(&timer->idle) &&
723 isc_time_compare(now,
724 &timer->idle) >= 0) {
725 idle = ISC_TRUE;
727 UNLOCK(&timer->lock);
728 if (idle) {
729 type = ISC_TIMEREVENT_IDLE;
730 post_event = ISC_TRUE;
731 need_schedule = ISC_FALSE;
732 } else {
734 * Idle timer has been touched;
735 * reschedule.
737 XTRACEID(isc_msgcat_get(isc_msgcat,
738 ISC_MSGSET_TIMER,
739 ISC_MSG_IDLERESCHED,
740 "idle reschedule"),
741 timer);
742 post_event = ISC_FALSE;
743 need_schedule = ISC_TRUE;
747 if (post_event) {
748 XTRACEID(isc_msgcat_get(isc_msgcat,
749 ISC_MSGSET_TIMER,
750 ISC_MSG_POSTING,
751 "posting"), timer);
753 * XXX We could preallocate this event.
755 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
756 timer,
757 type,
758 timer->action,
759 timer->arg,
760 sizeof(*event));
762 if (event != NULL) {
763 event->due = timer->due;
764 isc_task_send(timer->task,
765 ISC_EVENT_PTR(&event));
766 } else
767 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
768 isc_msgcat_get(isc_msgcat,
769 ISC_MSGSET_TIMER,
770 ISC_MSG_EVENTNOTALLOC,
771 "couldn't "
772 "allocate event"));
775 timer->index = 0;
776 isc_heap_delete(manager->heap, 1);
777 manager->nscheduled--;
779 if (need_schedule) {
780 result = schedule(timer, now, ISC_FALSE);
781 if (result != ISC_R_SUCCESS)
782 UNEXPECTED_ERROR(__FILE__, __LINE__,
783 "%s: %u",
784 isc_msgcat_get(isc_msgcat,
785 ISC_MSGSET_TIMER,
786 ISC_MSG_SCHEDFAIL,
787 "couldn't schedule "
788 "timer"),
789 result);
791 } else {
792 manager->due = timer->due;
793 done = ISC_TRUE;
798 #ifdef USE_TIMER_THREAD
799 static isc_threadresult_t
800 #ifdef _WIN32 /* XXXDCL */
801 WINAPI
802 #endif
803 run(void *uap) {
804 isc__timermgr_t *manager = uap;
805 isc_time_t now;
806 isc_result_t result;
808 LOCK(&manager->lock);
809 while (!manager->done) {
810 TIME_NOW(&now);
812 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
813 ISC_MSG_RUNNING,
814 "running"), now);
816 dispatch(manager, &now);
818 if (manager->nscheduled > 0) {
819 XTRACETIME2(isc_msgcat_get(isc_msgcat,
820 ISC_MSGSET_GENERAL,
821 ISC_MSG_WAITUNTIL,
822 "waituntil"),
823 manager->due, now);
824 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
825 INSIST(result == ISC_R_SUCCESS ||
826 result == ISC_R_TIMEDOUT);
827 } else {
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);
837 #ifdef OPENSSL_LEAKS
838 ERR_remove_state(0);
839 #endif
841 return ((isc_threadresult_t)0);
843 #endif /* USE_TIMER_THREAD */
845 static isc_boolean_t
846 sooner(void *v1, void *v2) {
847 isc__timer_t *t1, *t2;
849 t1 = v1;
850 t2 = v2;
851 REQUIRE(VALID_TIMER(t1));
852 REQUIRE(VALID_TIMER(t2));
854 if (isc_time_compare(&t1->due, &t2->due) < 0)
855 return (ISC_TRUE);
856 return (ISC_FALSE);
859 static void
860 set_index(void *what, unsigned int index) {
861 isc__timer_t *timer;
863 timer = what;
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;
872 isc_result_t result;
875 * Create a timer manager.
878 REQUIRE(managerp != NULL && *managerp == NULL);
880 #ifdef USE_SHARED_MANAGER
881 if (timermgr != NULL) {
882 timermgr->refs++;
883 *managerp = (isc_timermgr_t *)timermgr;
884 return (ISC_R_SUCCESS);
886 #endif /* USE_SHARED_MANAGER */
888 manager = isc_mem_get(mctx, sizeof(*manager));
889 if (manager == NULL)
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));
911 return (result);
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) !=
927 ISC_R_SUCCESS) {
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);
939 #endif
940 #ifdef USE_SHARED_MANAGER
941 manager->refs = 1;
942 timermgr = 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);
958 #else
959 UNUSED(manager0);
960 #endif
963 ISC_TIMERFUNC_SCOPE void
964 isc__timermgr_destroy(isc_timermgr_t **managerp) {
965 isc__timermgr_t *manager;
966 isc_mem_t *mctx;
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) {
980 manager->refs--;
981 UNLOCK(&manager->lock);
982 *managerp = NULL;
983 return;
985 #endif /* USE_SHARED_MANAGER */
987 #ifndef USE_TIMER_THREAD
988 isc__timermgr_dispatch((isc_timermgr_t *)manager);
989 #endif
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 */
1014 * Clean up.
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);
1027 *managerp = NULL;
1029 #ifdef USE_SHARED_MANAGER
1030 timermgr = NULL;
1031 #endif
1034 #ifndef USE_TIMER_THREAD
1035 isc_result_t
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)
1041 manager = timermgr;
1042 #endif
1043 if (manager == NULL || manager->nscheduled == 0)
1044 return (ISC_R_NOTFOUND);
1045 *when = manager->due;
1046 return (ISC_R_SUCCESS);
1049 void
1050 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
1051 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1052 isc_time_t now;
1054 #ifdef USE_SHARED_MANAGER
1055 if (manager == NULL)
1056 manager = timermgr;
1057 #endif
1058 if (manager == NULL)
1059 return;
1060 TIME_NOW(&now);
1061 dispatch(manager, &now);
1063 #endif /* USE_TIMER_THREAD */
1065 #ifdef USE_TIMERIMPREGISTER
1066 isc_result_t
1067 isc__timer_register() {
1068 return (isc_timer_register(isc__timermgr_create));
1070 #endif