Remove building with NOCRYPTO option
[minix.git] / external / bsd / bind / dist / lib / isc / timer.c
blobaa3bfa76e313162311a6fa604251f28fe8cc18c8
1 /* $NetBSD: timer.c,v 1.10 2014/12/10 04:37:59 christos Exp $ */
3 /*
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.
20 /* Id */
22 /*! \file */
24 #include <config.h>
26 #include <isc/app.h>
27 #include <isc/condition.h>
28 #include <isc/heap.h>
29 #include <isc/log.h>
30 #include <isc/magic.h>
31 #include <isc/mem.h>
32 #include <isc/msgs.h>
33 #include <isc/once.h>
34 #include <isc/platform.h>
35 #include <isc/task.h>
36 #include <isc/thread.h>
37 #include <isc/time.h>
38 #include <isc/timer.h>
39 #include <isc/util.h>
41 #ifdef OPENSSL_LEAKS
42 #include <openssl/err.h>
43 #endif
45 /* See task.c about the following definition: */
46 #ifdef ISC_PLATFORM_USETHREADS
47 #define USE_TIMER_THREAD
48 #else
49 #define USE_SHARED_MANAGER
50 #endif /* ISC_PLATFORM_USETHREADS */
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 following are intended for internal use (indicated by "isc__"
124 * prefix) but are not declared as static, allowing direct access from
125 * unit tests etc.
128 isc_result_t
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);
133 isc_result_t
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);
137 isc_timertype_t
138 isc_timer_gettype(isc_timer_t *timer);
139 isc_result_t
140 isc__timer_touch(isc_timer_t *timer);
141 void
142 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
143 void
144 isc__timer_detach(isc_timer_t **timerp);
145 isc_result_t
146 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
147 void
148 isc_timermgr_poke(isc_timermgr_t *manager0);
149 void
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.
158 void *gettype;
159 } timermethods = {
161 isc__timer_attach,
162 isc__timer_detach,
163 isc__timer_reset,
164 isc__timer_touch
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,
175 isc__timer_create
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) {
189 isc_result_t result;
190 isc__timermgr_t *manager;
191 isc_time_t due;
192 int cmp;
193 #ifdef USE_TIMER_THREAD
194 isc_boolean_t timedwait;
195 #endif
198 * Note: the caller must ensure locking.
201 REQUIRE(timer->type != isc_timertype_inactive);
203 #ifndef USE_TIMER_THREAD
204 UNUSED(signal_ok);
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);
216 #endif
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)
224 return (result);
225 if (timer->type == isc_timertype_limited &&
226 isc_time_compare(&timer->expires, &due) < 0)
227 due = timer->expires;
228 } else {
229 if (isc_time_isepoch(&timer->idle))
230 due = timer->expires;
231 else if (isc_time_isepoch(&timer->expires))
232 due = timer->idle;
233 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
234 due = timer->idle;
235 else
236 due = timer->expires;
240 * Schedule the timer.
243 if (timer->index > 0) {
245 * Already scheduled.
247 cmp = isc_time_compare(&due, &timer->due);
248 timer->due = due;
249 switch (cmp) {
250 case -1:
251 isc_heap_increased(manager->heap, timer->index);
252 break;
253 case 1:
254 isc_heap_decreased(manager->heap, timer->index);
255 break;
256 case 0:
257 /* Nothing to do. */
258 break;
260 } else {
261 timer->due = due;
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;
291 isc_time_t then;
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,
308 ISC_MSG_SIGNALSCHED,
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);
321 static inline void
322 deschedule(isc__timer_t *timer) {
323 #ifdef USE_TIMER_THREAD
324 isc_boolean_t need_wakeup = ISC_FALSE;
325 #endif
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;
337 #endif
338 isc_heap_delete(manager->heap, timer->index);
339 timer->index = 0;
340 INSIST(manager->nscheduled > 0);
341 manager->nscheduled--;
342 #ifdef USE_TIMER_THREAD
343 if (need_wakeup) {
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 */
353 static void
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,
364 timer,
365 ISC_TIMEREVENT_FIRSTEVENT,
366 ISC_TIMEREVENT_LASTEVENT,
367 NULL);
368 deschedule(timer);
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));
380 isc_result_t
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;
387 isc__timer_t *timer;
388 isc_result_t result;
389 isc_time_t now;
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
396 * in 'timerp'.
399 REQUIRE(VALID_MANAGER(manager));
400 REQUIRE(task != NULL);
401 REQUIRE(action != NULL);
402 if (expires == 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)));
413 * Get current time.
415 if (type != isc_timertype_inactive) {
416 TIME_NOW(&now);
417 } else {
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));
428 if (timer == NULL)
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));
438 return (result);
440 } else
441 isc_time_settoepoch(&timer->idle);
443 timer->type = type;
444 timer->expires = *expires;
445 timer->interval = *interval;
446 timer->task = NULL;
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);
460 timer->index = 0;
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));
465 return (result);
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);
481 else
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));
494 return (result);
497 *timerp = (isc_timer_t *)timer;
499 return (ISC_R_SUCCESS);
502 isc_result_t
503 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
504 const isc_time_t *expires, const isc_interval_t *interval,
505 isc_boolean_t purge)
507 isc__timer_t *timer = (isc__timer_t *)timer0;
508 isc_time_t now;
509 isc__timermgr_t *manager;
510 isc_result_t result;
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));
522 if (expires == NULL)
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)));
532 * Get current time.
534 if (type != isc_timertype_inactive) {
535 TIME_NOW(&now);
536 } else {
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);
546 LOCK(&timer->lock);
548 if (purge)
549 (void)isc_task_purgerange(timer->task,
550 timer,
551 ISC_TIMEREVENT_FIRSTEVENT,
552 ISC_TIMEREVENT_LASTEVENT,
553 NULL);
554 timer->type = type;
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);
559 } else {
560 isc_time_settoepoch(&timer->idle);
561 result = ISC_R_SUCCESS;
564 if (result == ISC_R_SUCCESS) {
565 if (type == isc_timertype_inactive) {
566 deschedule(timer);
567 result = ISC_R_SUCCESS;
568 } else
569 result = schedule(timer, &now, ISC_TRUE);
572 UNLOCK(&timer->lock);
573 UNLOCK(&manager->lock);
575 return (result);
578 isc_timertype_t
579 isc_timer_gettype(isc_timer_t *timer0) {
580 isc__timer_t *timer = (isc__timer_t *)timer0;
581 isc_timertype_t t;
583 REQUIRE(VALID_TIMER(timer));
585 LOCK(&timer->lock);
586 t = timer->type;
587 UNLOCK(&timer->lock);
589 return (t);
592 isc_result_t
593 isc__timer_touch(isc_timer_t *timer0) {
594 isc__timer_t *timer = (isc__timer_t *)timer0;
595 isc_result_t result;
596 isc_time_t now;
599 * Set the last-touched time of 'timer' to the current time.
602 REQUIRE(VALID_TIMER(timer));
604 LOCK(&timer->lock);
607 * We'd like to
609 * REQUIRE(timer->type == isc_timertype_once);
611 * but we cannot without locking the manager lock too, which we
612 * don't want to do.
615 TIME_NOW(&now);
616 result = isc_time_add(&now, &timer->interval, &timer->idle);
618 UNLOCK(&timer->lock);
620 return (result);
623 void
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);
634 LOCK(&timer->lock);
635 timer->references++;
636 UNLOCK(&timer->lock);
638 *timerp = (isc_timer_t *)timer;
641 void
642 isc__timer_detach(isc_timer_t **timerp) {
643 isc__timer_t *timer;
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));
654 LOCK(&timer->lock);
655 REQUIRE(timer->references > 0);
656 timer->references--;
657 if (timer->references == 0)
658 free_timer = ISC_TRUE;
659 UNLOCK(&timer->lock);
661 if (free_timer)
662 destroy(timer);
664 *timerp = NULL;
667 static void
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;
672 isc__timer_t *timer;
673 isc_result_t result;
674 isc_boolean_t idle;
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) {
689 int cmp;
690 cmp = isc_time_compare(now, &timer->expires);
691 if (cmp >= 0) {
692 type = ISC_TIMEREVENT_LIFE;
693 post_event = ISC_TRUE;
694 need_schedule = ISC_FALSE;
695 } else {
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;
706 } else {
707 idle = ISC_FALSE;
709 LOCK(&timer->lock);
710 if (!isc_time_isepoch(&timer->idle) &&
711 isc_time_compare(now,
712 &timer->idle) >= 0) {
713 idle = ISC_TRUE;
715 UNLOCK(&timer->lock);
716 if (idle) {
717 type = ISC_TIMEREVENT_IDLE;
718 post_event = ISC_TRUE;
719 need_schedule = ISC_FALSE;
720 } else {
722 * Idle timer has been touched;
723 * reschedule.
725 XTRACEID(isc_msgcat_get(isc_msgcat,
726 ISC_MSGSET_TIMER,
727 ISC_MSG_IDLERESCHED,
728 "idle reschedule"),
729 timer);
730 post_event = ISC_FALSE;
731 need_schedule = ISC_TRUE;
735 if (post_event) {
736 XTRACEID(isc_msgcat_get(isc_msgcat,
737 ISC_MSGSET_TIMER,
738 ISC_MSG_POSTING,
739 "posting"), timer);
741 * XXX We could preallocate this event.
743 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
744 timer,
745 type,
746 timer->action,
747 timer->arg,
748 sizeof(*event));
750 if (event != NULL) {
751 event->due = timer->due;
752 isc_task_send(timer->task,
753 ISC_EVENT_PTR(&event));
754 } else
755 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
756 isc_msgcat_get(isc_msgcat,
757 ISC_MSGSET_TIMER,
758 ISC_MSG_EVENTNOTALLOC,
759 "couldn't "
760 "allocate event"));
763 timer->index = 0;
764 isc_heap_delete(manager->heap, 1);
765 manager->nscheduled--;
767 if (need_schedule) {
768 result = schedule(timer, now, ISC_FALSE);
769 if (result != ISC_R_SUCCESS)
770 UNEXPECTED_ERROR(__FILE__, __LINE__,
771 "%s: %u",
772 isc_msgcat_get(isc_msgcat,
773 ISC_MSGSET_TIMER,
774 ISC_MSG_SCHEDFAIL,
775 "couldn't schedule "
776 "timer"),
777 result);
779 } else {
780 manager->due = timer->due;
781 done = ISC_TRUE;
786 #ifdef USE_TIMER_THREAD
787 static isc_threadresult_t
788 #ifdef _WIN32 /* XXXDCL */
789 WINAPI
790 #endif
791 run(void *uap) {
792 isc__timermgr_t *manager = uap;
793 isc_time_t now;
794 isc_result_t result;
796 LOCK(&manager->lock);
797 while (!manager->done) {
798 TIME_NOW(&now);
800 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
801 ISC_MSG_RUNNING,
802 "running"), now);
804 dispatch(manager, &now);
806 if (manager->nscheduled > 0) {
807 XTRACETIME2(isc_msgcat_get(isc_msgcat,
808 ISC_MSGSET_GENERAL,
809 ISC_MSG_WAITUNTIL,
810 "waituntil"),
811 manager->due, now);
812 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
813 INSIST(result == ISC_R_SUCCESS ||
814 result == ISC_R_TIMEDOUT);
815 } else {
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);
825 #ifdef OPENSSL_LEAKS
826 ERR_remove_state(0);
827 #endif
829 return ((isc_threadresult_t)0);
831 #endif /* USE_TIMER_THREAD */
833 static isc_boolean_t
834 sooner(void *v1, void *v2) {
835 isc__timer_t *t1, *t2;
837 t1 = v1;
838 t2 = v2;
839 REQUIRE(VALID_TIMER(t1));
840 REQUIRE(VALID_TIMER(t2));
842 if (isc_time_compare(&t1->due, &t2->due) < 0)
843 return (ISC_TRUE);
844 return (ISC_FALSE);
847 static void
848 set_index(void *what, unsigned int index) {
849 isc__timer_t *timer;
851 timer = what;
852 REQUIRE(VALID_TIMER(timer));
854 timer->index = index;
857 isc_result_t
858 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
859 isc__timermgr_t *manager;
860 isc_result_t result;
863 * Create a timer manager.
866 REQUIRE(managerp != NULL && *managerp == NULL);
868 #ifdef USE_SHARED_MANAGER
869 if (timermgr != NULL) {
870 timermgr->refs++;
871 *managerp = (isc_timermgr_t *)timermgr;
872 return (ISC_R_SUCCESS);
874 #endif /* USE_SHARED_MANAGER */
876 manager = isc_mem_get(mctx, sizeof(*manager));
877 if (manager == NULL)
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));
899 return (result);
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) !=
915 ISC_R_SUCCESS) {
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);
927 #endif
928 #ifdef USE_SHARED_MANAGER
929 manager->refs = 1;
930 timermgr = manager;
931 #endif /* USE_SHARED_MANAGER */
933 *managerp = (isc_timermgr_t *)manager;
935 return (ISC_R_SUCCESS);
938 void
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);
946 #else
947 UNUSED(manager0);
948 #endif
951 void
952 isc__timermgr_destroy(isc_timermgr_t **managerp) {
953 isc__timermgr_t *manager;
954 isc_mem_t *mctx;
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
967 manager->refs--;
968 if (manager->refs > 0) {
969 UNLOCK(&manager->lock);
970 *managerp = NULL;
971 return;
973 timermgr = NULL;
974 #endif /* USE_SHARED_MANAGER */
976 #ifndef USE_TIMER_THREAD
977 isc__timermgr_dispatch((isc_timermgr_t *)manager);
978 #endif
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 */
1003 * Clean up.
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);
1016 *managerp = NULL;
1018 #ifdef USE_SHARED_MANAGER
1019 timermgr = NULL;
1020 #endif
1023 #ifndef USE_TIMER_THREAD
1024 isc_result_t
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)
1030 manager = timermgr;
1031 #endif
1032 if (manager == NULL || manager->nscheduled == 0)
1033 return (ISC_R_NOTFOUND);
1034 *when = manager->due;
1035 return (ISC_R_SUCCESS);
1038 void
1039 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
1040 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1041 isc_time_t now;
1043 #ifdef USE_SHARED_MANAGER
1044 if (manager == NULL)
1045 manager = timermgr;
1046 #endif
1047 if (manager == NULL)
1048 return;
1049 TIME_NOW(&now);
1050 dispatch(manager, &now);
1052 #endif /* USE_TIMER_THREAD */
1054 isc_result_t
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;
1063 static void
1064 initialize(void) {
1065 RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS);
1068 isc_result_t
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);
1074 LOCK(&createlock);
1075 if (timermgr_createfunc == NULL)
1076 timermgr_createfunc = createfunc;
1077 else
1078 result = ISC_R_EXISTS;
1079 UNLOCK(&createlock);
1081 return (result);
1084 isc_result_t
1085 isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx,
1086 isc_timermgr_t **managerp)
1088 isc_result_t result;
1090 LOCK(&createlock);
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);
1100 return (result);
1103 isc_result_t
1104 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
1105 isc_result_t result;
1107 if (isc_bind9)
1108 return (isc__timermgr_create(mctx, managerp));
1110 LOCK(&createlock);
1112 REQUIRE(timermgr_createfunc != NULL);
1113 result = (*timermgr_createfunc)(mctx, managerp);
1115 UNLOCK(&createlock);
1117 return (result);
1120 void
1121 isc_timermgr_destroy(isc_timermgr_t **managerp) {
1122 REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp));
1124 if (isc_bind9)
1125 isc__timermgr_destroy(managerp);
1126 else
1127 (*managerp)->methods->destroy(managerp);
1129 ENSURE(*managerp == NULL);
1132 isc_result_t
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));
1140 if (isc_bind9)
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,
1146 timerp));
1149 void
1150 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
1151 REQUIRE(ISCAPI_TIMER_VALID(timer));
1152 REQUIRE(timerp != NULL && *timerp == NULL);
1154 if (isc_bind9)
1155 isc__timer_attach(timer, timerp);
1156 else
1157 timer->methods->attach(timer, timerp);
1159 ENSURE(*timerp == timer);
1162 void
1163 isc_timer_detach(isc_timer_t **timerp) {
1164 REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp));
1166 if (isc_bind9)
1167 isc__timer_detach(timerp);
1168 else
1169 (*timerp)->methods->detach(timerp);
1171 ENSURE(*timerp == NULL);
1174 isc_result_t
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));
1181 if (isc_bind9)
1182 return (isc__timer_reset(timer, type, expires,
1183 interval, purge));
1185 return (timer->methods->reset(timer, type, expires, interval, purge));
1188 isc_result_t
1189 isc_timer_touch(isc_timer_t *timer) {
1190 REQUIRE(ISCAPI_TIMER_VALID(timer));
1192 if (isc_bind9)
1193 return (isc__timer_touch(timer));
1195 return (timer->methods->touch(timer));