]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/isc/timer.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / isc / timer.c
1 /*
2  * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2002  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id$ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <isc/condition.h>
25 #include <isc/heap.h>
26 #include <isc/log.h>
27 #include <isc/magic.h>
28 #include <isc/mem.h>
29 #include <isc/msgs.h>
30 #include <isc/platform.h>
31 #include <isc/task.h>
32 #include <isc/thread.h>
33 #include <isc/time.h>
34 #include <isc/timer.h>
35 #include <isc/util.h>
36
37 #ifdef OPENSSL_LEAKS
38 #include <openssl/err.h>
39 #endif
40
41 /* See task.c about the following definition: */
42 #ifdef BIND9
43 #ifdef ISC_PLATFORM_USETHREADS
44 #define USE_TIMER_THREAD
45 #else
46 #define USE_SHARED_MANAGER
47 #endif  /* ISC_PLATFORM_USETHREADS */
48 #endif  /* BIND9 */
49
50 #ifndef USE_TIMER_THREAD
51 #include "timer_p.h"
52 #endif /* USE_TIMER_THREAD */
53
54 #ifdef ISC_TIMER_TRACE
55 #define XTRACE(s)                       fprintf(stderr, "%s\n", (s))
56 #define XTRACEID(s, t)                  fprintf(stderr, "%s %p\n", (s), (t))
57 #define XTRACETIME(s, d)                fprintf(stderr, "%s %u.%09u\n", (s), \
58                                                (d).seconds, (d).nanoseconds)
59 #define XTRACETIME2(s, d, n)            fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
60                                                (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
61 #define XTRACETIMER(s, t, d)            fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
62                                                (d).seconds, (d).nanoseconds)
63 #else
64 #define XTRACE(s)
65 #define XTRACEID(s, t)
66 #define XTRACETIME(s, d)
67 #define XTRACETIME2(s, d, n)
68 #define XTRACETIMER(s, t, d)
69 #endif /* ISC_TIMER_TRACE */
70
71 #define TIMER_MAGIC                     ISC_MAGIC('T', 'I', 'M', 'R')
72 #define VALID_TIMER(t)                  ISC_MAGIC_VALID(t, TIMER_MAGIC)
73
74 typedef struct isc__timer isc__timer_t;
75 typedef struct isc__timermgr isc__timermgr_t;
76
77 struct isc__timer {
78         /*! Not locked. */
79         isc_timer_t                     common;
80         isc__timermgr_t *               manager;
81         isc_mutex_t                     lock;
82         /*! Locked by timer lock. */
83         unsigned int                    references;
84         isc_time_t                      idle;
85         /*! Locked by manager lock. */
86         isc_timertype_t                 type;
87         isc_time_t                      expires;
88         isc_interval_t                  interval;
89         isc_task_t *                    task;
90         isc_taskaction_t                action;
91         void *                          arg;
92         unsigned int                    index;
93         isc_time_t                      due;
94         LINK(isc__timer_t)              link;
95 };
96
97 #define TIMER_MANAGER_MAGIC             ISC_MAGIC('T', 'I', 'M', 'M')
98 #define VALID_MANAGER(m)                ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
99
100 struct isc__timermgr {
101         /* Not locked. */
102         isc_timermgr_t                  common;
103         isc_mem_t *                     mctx;
104         isc_mutex_t                     lock;
105         /* Locked by manager lock. */
106         isc_boolean_t                   done;
107         LIST(isc__timer_t)              timers;
108         unsigned int                    nscheduled;
109         isc_time_t                      due;
110 #ifdef USE_TIMER_THREAD
111         isc_condition_t                 wakeup;
112         isc_thread_t                    thread;
113 #endif  /* USE_TIMER_THREAD */
114 #ifdef USE_SHARED_MANAGER
115         unsigned int                    refs;
116 #endif /* USE_SHARED_MANAGER */
117         isc_heap_t *                    heap;
118 };
119
120 /*%
121  * The followings can be either static or public, depending on build
122  * environment.
123  */
124
125 #ifdef BIND9
126 #define ISC_TIMERFUNC_SCOPE
127 #else
128 #define ISC_TIMERFUNC_SCOPE static
129 #endif
130
131 ISC_TIMERFUNC_SCOPE isc_result_t
132 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
133                   isc_time_t *expires, isc_interval_t *interval,
134                   isc_task_t *task, isc_taskaction_t action, const void *arg,
135                   isc_timer_t **timerp);
136 ISC_TIMERFUNC_SCOPE isc_result_t
137 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
138                  isc_time_t *expires, isc_interval_t *interval,
139                  isc_boolean_t purge);
140 ISC_TIMERFUNC_SCOPE isc_timertype_t
141 isc__timer_gettype(isc_timer_t *timer);
142 ISC_TIMERFUNC_SCOPE isc_result_t
143 isc__timer_touch(isc_timer_t *timer);
144 ISC_TIMERFUNC_SCOPE void
145 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
146 ISC_TIMERFUNC_SCOPE void
147 isc__timer_detach(isc_timer_t **timerp);
148 ISC_TIMERFUNC_SCOPE isc_result_t
149 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
150 ISC_TIMERFUNC_SCOPE void
151 isc__timermgr_poke(isc_timermgr_t *manager0);
152 ISC_TIMERFUNC_SCOPE void
153 isc__timermgr_destroy(isc_timermgr_t **managerp);
154
155 static struct isc__timermethods {
156         isc_timermethods_t methods;
157
158         /*%
159          * The following are defined just for avoiding unused static functions.
160          */
161 #ifndef BIND9
162         void *gettype;
163 #endif
164 } timermethods = {
165         {
166                 isc__timer_attach,
167                 isc__timer_detach,
168                 isc__timer_reset,
169                 isc__timer_touch
170         }
171 #ifndef BIND9
172         ,
173         (void *)isc__timer_gettype
174 #endif
175 };
176
177 static struct isc__timermgrmethods {
178         isc_timermgrmethods_t methods;
179 #ifndef BIND9
180         void *poke;             /* see above */
181 #endif
182 } timermgrmethods = {
183         {
184                 isc__timermgr_destroy,
185                 isc__timer_create
186         }
187 #ifndef BIND9
188         ,
189         (void *)isc__timermgr_poke
190 #endif
191 };
192
193 #ifdef USE_SHARED_MANAGER
194 /*!
195  * If the manager is supposed to be shared, there can be only one.
196  */
197 static isc__timermgr_t *timermgr = NULL;
198 #endif /* USE_SHARED_MANAGER */
199
200 static inline isc_result_t
201 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
202         isc_result_t result;
203         isc__timermgr_t *manager;
204         isc_time_t due;
205         int cmp;
206 #ifdef USE_TIMER_THREAD
207         isc_boolean_t timedwait;
208 #endif
209
210         /*!
211          * Note: the caller must ensure locking.
212          */
213
214         REQUIRE(timer->type != isc_timertype_inactive);
215
216 #ifndef USE_TIMER_THREAD
217         UNUSED(signal_ok);
218 #endif /* USE_TIMER_THREAD */
219
220         manager = timer->manager;
221
222 #ifdef USE_TIMER_THREAD
223         /*!
224          * If the manager was timed wait, we may need to signal the
225          * manager to force a wakeup.
226          */
227         timedwait = ISC_TF(manager->nscheduled > 0 &&
228                            isc_time_seconds(&manager->due) != 0);
229 #endif
230
231         /*
232          * Compute the new due time.
233          */
234         if (timer->type != isc_timertype_once) {
235                 result = isc_time_add(now, &timer->interval, &due);
236                 if (result != ISC_R_SUCCESS)
237                         return (result);
238                 if (timer->type == isc_timertype_limited &&
239                     isc_time_compare(&timer->expires, &due) < 0)
240                         due = timer->expires;
241         } else {
242                 if (isc_time_isepoch(&timer->idle))
243                         due = timer->expires;
244                 else if (isc_time_isepoch(&timer->expires))
245                         due = timer->idle;
246                 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
247                         due = timer->idle;
248                 else
249                         due = timer->expires;
250         }
251
252         /*
253          * Schedule the timer.
254          */
255
256         if (timer->index > 0) {
257                 /*
258                  * Already scheduled.
259                  */
260                 cmp = isc_time_compare(&due, &timer->due);
261                 timer->due = due;
262                 switch (cmp) {
263                 case -1:
264                         isc_heap_increased(manager->heap, timer->index);
265                         break;
266                 case 1:
267                         isc_heap_decreased(manager->heap, timer->index);
268                         break;
269                 case 0:
270                         /* Nothing to do. */
271                         break;
272                 }
273         } else {
274                 timer->due = due;
275                 result = isc_heap_insert(manager->heap, timer);
276                 if (result != ISC_R_SUCCESS) {
277                         INSIST(result == ISC_R_NOMEMORY);
278                         return (ISC_R_NOMEMORY);
279                 }
280                 manager->nscheduled++;
281         }
282
283         XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
284                                    ISC_MSG_SCHEDULE, "schedule"), timer, due);
285
286         /*
287          * If this timer is at the head of the queue, we need to ensure
288          * that we won't miss it if it has a more recent due time than
289          * the current "next" timer.  We do this either by waking up the
290          * run thread, or explicitly setting the value in the manager.
291          */
292 #ifdef USE_TIMER_THREAD
293
294         /*
295          * This is a temporary (probably) hack to fix a bug on tru64 5.1
296          * and 5.1a.  Sometimes, pthread_cond_timedwait() doesn't actually
297          * return when the time expires, so here, we check to see if
298          * we're 15 seconds or more behind, and if we are, we signal
299          * the dispatcher.  This isn't such a bad idea as a general purpose
300          * watchdog, so perhaps we should just leave it in here.
301          */
302         if (signal_ok && timedwait) {
303                 isc_interval_t fifteen;
304                 isc_time_t then;
305
306                 isc_interval_set(&fifteen, 15, 0);
307                 result = isc_time_add(&manager->due, &fifteen, &then);
308
309                 if (result == ISC_R_SUCCESS &&
310                     isc_time_compare(&then, now) < 0) {
311                         SIGNAL(&manager->wakeup);
312                         signal_ok = ISC_FALSE;
313                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
314                                       ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
315                                       "*** POKED TIMER ***");
316                 }
317         }
318
319         if (timer->index == 1 && signal_ok) {
320                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
321                                       ISC_MSG_SIGNALSCHED,
322                                       "signal (schedule)"));
323                 SIGNAL(&manager->wakeup);
324         }
325 #else /* USE_TIMER_THREAD */
326         if (timer->index == 1 &&
327             isc_time_compare(&timer->due, &manager->due) < 0)
328                 manager->due = timer->due;
329 #endif /* USE_TIMER_THREAD */
330
331         return (ISC_R_SUCCESS);
332 }
333
334 static inline void
335 deschedule(isc__timer_t *timer) {
336 #ifdef USE_TIMER_THREAD
337         isc_boolean_t need_wakeup = ISC_FALSE;
338 #endif
339         isc__timermgr_t *manager;
340
341         /*
342          * The caller must ensure locking.
343          */
344
345         manager = timer->manager;
346         if (timer->index > 0) {
347 #ifdef USE_TIMER_THREAD
348                 if (timer->index == 1)
349                         need_wakeup = ISC_TRUE;
350 #endif
351                 isc_heap_delete(manager->heap, timer->index);
352                 timer->index = 0;
353                 INSIST(manager->nscheduled > 0);
354                 manager->nscheduled--;
355 #ifdef USE_TIMER_THREAD
356                 if (need_wakeup) {
357                         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
358                                               ISC_MSG_SIGNALDESCHED,
359                                               "signal (deschedule)"));
360                         SIGNAL(&manager->wakeup);
361                 }
362 #endif /* USE_TIMER_THREAD */
363         }
364 }
365
366 static void
367 destroy(isc__timer_t *timer) {
368         isc__timermgr_t *manager = timer->manager;
369
370         /*
371          * The caller must ensure it is safe to destroy the timer.
372          */
373
374         LOCK(&manager->lock);
375
376         (void)isc_task_purgerange(timer->task,
377                                   timer,
378                                   ISC_TIMEREVENT_FIRSTEVENT,
379                                   ISC_TIMEREVENT_LASTEVENT,
380                                   NULL);
381         deschedule(timer);
382         UNLINK(manager->timers, timer, link);
383
384         UNLOCK(&manager->lock);
385
386         isc_task_detach(&timer->task);
387         DESTROYLOCK(&timer->lock);
388         timer->common.impmagic = 0;
389         timer->common.magic = 0;
390         isc_mem_put(manager->mctx, timer, sizeof(*timer));
391 }
392
393 ISC_TIMERFUNC_SCOPE isc_result_t
394 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
395                   isc_time_t *expires, isc_interval_t *interval,
396                   isc_task_t *task, isc_taskaction_t action, const void *arg,
397                   isc_timer_t **timerp)
398 {
399         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
400         isc__timer_t *timer;
401         isc_result_t result;
402         isc_time_t now;
403
404         /*
405          * Create a new 'type' timer managed by 'manager'.  The timers
406          * parameters are specified by 'expires' and 'interval'.  Events
407          * will be posted to 'task' and when dispatched 'action' will be
408          * called with 'arg' as the arg value.  The new timer is returned
409          * in 'timerp'.
410          */
411
412         REQUIRE(VALID_MANAGER(manager));
413         REQUIRE(task != NULL);
414         REQUIRE(action != NULL);
415         if (expires == NULL)
416                 expires = isc_time_epoch;
417         if (interval == NULL)
418                 interval = isc_interval_zero;
419         REQUIRE(type == isc_timertype_inactive ||
420                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
421         REQUIRE(timerp != NULL && *timerp == NULL);
422         REQUIRE(type != isc_timertype_limited ||
423                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
424
425         /*
426          * Get current time.
427          */
428         if (type != isc_timertype_inactive) {
429                 TIME_NOW(&now);
430         } else {
431                 /*
432                  * We don't have to do this, but it keeps the compiler from
433                  * complaining about "now" possibly being used without being
434                  * set, even though it will never actually happen.
435                  */
436                 isc_time_settoepoch(&now);
437         }
438
439
440         timer = isc_mem_get(manager->mctx, sizeof(*timer));
441         if (timer == NULL)
442                 return (ISC_R_NOMEMORY);
443
444         timer->manager = manager;
445         timer->references = 1;
446
447         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
448                 result = isc_time_add(&now, interval, &timer->idle);
449                 if (result != ISC_R_SUCCESS) {
450                         isc_mem_put(manager->mctx, timer, sizeof(*timer));
451                         return (result);
452                 }
453         } else
454                 isc_time_settoepoch(&timer->idle);
455
456         timer->type = type;
457         timer->expires = *expires;
458         timer->interval = *interval;
459         timer->task = NULL;
460         isc_task_attach(task, &timer->task);
461         timer->action = action;
462         /*
463          * Removing the const attribute from "arg" is the best of two
464          * evils here.  If the timer->arg member is made const, then
465          * it affects a great many recipients of the timer event
466          * which did not pass in an "arg" that was truly const.
467          * Changing isc_timer_create() to not have "arg" prototyped as const,
468          * though, can cause compilers warnings for calls that *do*
469          * have a truly const arg.  The caller will have to carefully
470          * keep track of whether arg started as a true const.
471          */
472         DE_CONST(arg, timer->arg);
473         timer->index = 0;
474         result = isc_mutex_init(&timer->lock);
475         if (result != ISC_R_SUCCESS) {
476                 isc_task_detach(&timer->task);
477                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
478                 return (result);
479         }
480         ISC_LINK_INIT(timer, link);
481         timer->common.impmagic = TIMER_MAGIC;
482         timer->common.magic = ISCAPI_TIMER_MAGIC;
483         timer->common.methods = (isc_timermethods_t *)&timermethods;
484
485         LOCK(&manager->lock);
486
487         /*
488          * Note we don't have to lock the timer like we normally would because
489          * there are no external references to it yet.
490          */
491
492         if (type != isc_timertype_inactive)
493                 result = schedule(timer, &now, ISC_TRUE);
494         else
495                 result = ISC_R_SUCCESS;
496         if (result == ISC_R_SUCCESS)
497                 APPEND(manager->timers, timer, link);
498
499         UNLOCK(&manager->lock);
500
501         if (result != ISC_R_SUCCESS) {
502                 timer->common.impmagic = 0;
503                 timer->common.magic = 0;
504                 DESTROYLOCK(&timer->lock);
505                 isc_task_detach(&timer->task);
506                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
507                 return (result);
508         }
509
510         *timerp = (isc_timer_t *)timer;
511
512         return (ISC_R_SUCCESS);
513 }
514
515 ISC_TIMERFUNC_SCOPE isc_result_t
516 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
517                  isc_time_t *expires, isc_interval_t *interval,
518                  isc_boolean_t purge)
519 {
520         isc__timer_t *timer = (isc__timer_t *)timer0;
521         isc_time_t now;
522         isc__timermgr_t *manager;
523         isc_result_t result;
524
525         /*
526          * Change the timer's type, expires, and interval values to the given
527          * values.  If 'purge' is ISC_TRUE, any pending events from this timer
528          * are purged from its task's event queue.
529          */
530
531         REQUIRE(VALID_TIMER(timer));
532         manager = timer->manager;
533         REQUIRE(VALID_MANAGER(manager));
534
535         if (expires == NULL)
536                 expires = isc_time_epoch;
537         if (interval == NULL)
538                 interval = isc_interval_zero;
539         REQUIRE(type == isc_timertype_inactive ||
540                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
541         REQUIRE(type != isc_timertype_limited ||
542                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
543
544         /*
545          * Get current time.
546          */
547         if (type != isc_timertype_inactive) {
548                 TIME_NOW(&now);
549         } else {
550                 /*
551                  * We don't have to do this, but it keeps the compiler from
552                  * complaining about "now" possibly being used without being
553                  * set, even though it will never actually happen.
554                  */
555                 isc_time_settoepoch(&now);
556         }
557
558         LOCK(&manager->lock);
559         LOCK(&timer->lock);
560
561         if (purge)
562                 (void)isc_task_purgerange(timer->task,
563                                           timer,
564                                           ISC_TIMEREVENT_FIRSTEVENT,
565                                           ISC_TIMEREVENT_LASTEVENT,
566                                           NULL);
567         timer->type = type;
568         timer->expires = *expires;
569         timer->interval = *interval;
570         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
571                 result = isc_time_add(&now, interval, &timer->idle);
572         } else {
573                 isc_time_settoepoch(&timer->idle);
574                 result = ISC_R_SUCCESS;
575         }
576
577         if (result == ISC_R_SUCCESS) {
578                 if (type == isc_timertype_inactive) {
579                         deschedule(timer);
580                         result = ISC_R_SUCCESS;
581                 } else
582                         result = schedule(timer, &now, ISC_TRUE);
583         }
584
585         UNLOCK(&timer->lock);
586         UNLOCK(&manager->lock);
587
588         return (result);
589 }
590
591 ISC_TIMERFUNC_SCOPE isc_timertype_t
592 isc__timer_gettype(isc_timer_t *timer0) {
593         isc__timer_t *timer = (isc__timer_t *)timer0;
594         isc_timertype_t t;
595
596         REQUIRE(VALID_TIMER(timer));
597
598         LOCK(&timer->lock);
599         t = timer->type;
600         UNLOCK(&timer->lock);
601
602         return (t);
603 }
604
605 ISC_TIMERFUNC_SCOPE isc_result_t
606 isc__timer_touch(isc_timer_t *timer0) {
607         isc__timer_t *timer = (isc__timer_t *)timer0;
608         isc_result_t result;
609         isc_time_t now;
610
611         /*
612          * Set the last-touched time of 'timer' to the current time.
613          */
614
615         REQUIRE(VALID_TIMER(timer));
616
617         LOCK(&timer->lock);
618
619         /*
620          * We'd like to
621          *
622          *      REQUIRE(timer->type == isc_timertype_once);
623          *
624          * but we cannot without locking the manager lock too, which we
625          * don't want to do.
626          */
627
628         TIME_NOW(&now);
629         result = isc_time_add(&now, &timer->interval, &timer->idle);
630
631         UNLOCK(&timer->lock);
632
633         return (result);
634 }
635
636 ISC_TIMERFUNC_SCOPE void
637 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
638         isc__timer_t *timer = (isc__timer_t *)timer0;
639
640         /*
641          * Attach *timerp to timer.
642          */
643
644         REQUIRE(VALID_TIMER(timer));
645         REQUIRE(timerp != NULL && *timerp == NULL);
646
647         LOCK(&timer->lock);
648         timer->references++;
649         UNLOCK(&timer->lock);
650
651         *timerp = (isc_timer_t *)timer;
652 }
653
654 ISC_TIMERFUNC_SCOPE void
655 isc__timer_detach(isc_timer_t **timerp) {
656         isc__timer_t *timer;
657         isc_boolean_t free_timer = ISC_FALSE;
658
659         /*
660          * Detach *timerp from its timer.
661          */
662
663         REQUIRE(timerp != NULL);
664         timer = (isc__timer_t *)*timerp;
665         REQUIRE(VALID_TIMER(timer));
666
667         LOCK(&timer->lock);
668         REQUIRE(timer->references > 0);
669         timer->references--;
670         if (timer->references == 0)
671                 free_timer = ISC_TRUE;
672         UNLOCK(&timer->lock);
673
674         if (free_timer)
675                 destroy(timer);
676
677         *timerp = NULL;
678 }
679
680 static void
681 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
682         isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
683         isc_timerevent_t *event;
684         isc_eventtype_t type = 0;
685         isc__timer_t *timer;
686         isc_result_t result;
687         isc_boolean_t idle;
688
689         /*!
690          * The caller must be holding the manager lock.
691          */
692
693         while (manager->nscheduled > 0 && !done) {
694                 timer = isc_heap_element(manager->heap, 1);
695                 INSIST(timer->type != isc_timertype_inactive);
696                 if (isc_time_compare(now, &timer->due) >= 0) {
697                         if (timer->type == isc_timertype_ticker) {
698                                 type = ISC_TIMEREVENT_TICK;
699                                 post_event = ISC_TRUE;
700                                 need_schedule = ISC_TRUE;
701                         } else if (timer->type == isc_timertype_limited) {
702                                 int cmp;
703                                 cmp = isc_time_compare(now, &timer->expires);
704                                 if (cmp >= 0) {
705                                         type = ISC_TIMEREVENT_LIFE;
706                                         post_event = ISC_TRUE;
707                                         need_schedule = ISC_FALSE;
708                                 } else {
709                                         type = ISC_TIMEREVENT_TICK;
710                                         post_event = ISC_TRUE;
711                                         need_schedule = ISC_TRUE;
712                                 }
713                         } else if (!isc_time_isepoch(&timer->expires) &&
714                                    isc_time_compare(now,
715                                                     &timer->expires) >= 0) {
716                                 type = ISC_TIMEREVENT_LIFE;
717                                 post_event = ISC_TRUE;
718                                 need_schedule = ISC_FALSE;
719                         } else {
720                                 idle = ISC_FALSE;
721
722                                 LOCK(&timer->lock);
723                                 if (!isc_time_isepoch(&timer->idle) &&
724                                     isc_time_compare(now,
725                                                      &timer->idle) >= 0) {
726                                         idle = ISC_TRUE;
727                                 }
728                                 UNLOCK(&timer->lock);
729                                 if (idle) {
730                                         type = ISC_TIMEREVENT_IDLE;
731                                         post_event = ISC_TRUE;
732                                         need_schedule = ISC_FALSE;
733                                 } else {
734                                         /*
735                                          * Idle timer has been touched;
736                                          * reschedule.
737                                          */
738                                         XTRACEID(isc_msgcat_get(isc_msgcat,
739                                                                 ISC_MSGSET_TIMER,
740                                                                 ISC_MSG_IDLERESCHED,
741                                                                 "idle reschedule"),
742                                                  timer);
743                                         post_event = ISC_FALSE;
744                                         need_schedule = ISC_TRUE;
745                                 }
746                         }
747
748                         if (post_event) {
749                                 XTRACEID(isc_msgcat_get(isc_msgcat,
750                                                         ISC_MSGSET_TIMER,
751                                                         ISC_MSG_POSTING,
752                                                         "posting"), timer);
753                                 /*
754                                  * XXX We could preallocate this event.
755                                  */
756                                 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
757                                                            timer,
758                                                            type,
759                                                            timer->action,
760                                                            timer->arg,
761                                                            sizeof(*event));
762
763                                 if (event != NULL) {
764                                         event->due = timer->due;
765                                         isc_task_send(timer->task,
766                                                       ISC_EVENT_PTR(&event));
767                                 } else
768                                         UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
769                                                  isc_msgcat_get(isc_msgcat,
770                                                          ISC_MSGSET_TIMER,
771                                                          ISC_MSG_EVENTNOTALLOC,
772                                                          "couldn't "
773                                                          "allocate event"));
774                         }
775
776                         timer->index = 0;
777                         isc_heap_delete(manager->heap, 1);
778                         manager->nscheduled--;
779
780                         if (need_schedule) {
781                                 result = schedule(timer, now, ISC_FALSE);
782                                 if (result != ISC_R_SUCCESS)
783                                         UNEXPECTED_ERROR(__FILE__, __LINE__,
784                                                          "%s: %u",
785                                                 isc_msgcat_get(isc_msgcat,
786                                                         ISC_MSGSET_TIMER,
787                                                         ISC_MSG_SCHEDFAIL,
788                                                         "couldn't schedule "
789                                                         "timer"),
790                                                          result);
791                         }
792                 } else {
793                         manager->due = timer->due;
794                         done = ISC_TRUE;
795                 }
796         }
797 }
798
799 #ifdef USE_TIMER_THREAD
800 static isc_threadresult_t
801 #ifdef _WIN32                   /* XXXDCL */
802 WINAPI
803 #endif
804 run(void *uap) {
805         isc__timermgr_t *manager = uap;
806         isc_time_t now;
807         isc_result_t result;
808
809         LOCK(&manager->lock);
810         while (!manager->done) {
811                 TIME_NOW(&now);
812
813                 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
814                                           ISC_MSG_RUNNING,
815                                           "running"), now);
816
817                 dispatch(manager, &now);
818
819                 if (manager->nscheduled > 0) {
820                         XTRACETIME2(isc_msgcat_get(isc_msgcat,
821                                                    ISC_MSGSET_GENERAL,
822                                                    ISC_MSG_WAITUNTIL,
823                                                    "waituntil"),
824                                     manager->due, now);
825                         result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
826                         INSIST(result == ISC_R_SUCCESS ||
827                                result == ISC_R_TIMEDOUT);
828                 } else {
829                         XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
830                                                   ISC_MSG_WAIT, "wait"), now);
831                         WAIT(&manager->wakeup, &manager->lock);
832                 }
833                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
834                                       ISC_MSG_WAKEUP, "wakeup"));
835         }
836         UNLOCK(&manager->lock);
837
838 #ifdef OPENSSL_LEAKS
839         ERR_remove_state(0);
840 #endif
841
842         return ((isc_threadresult_t)0);
843 }
844 #endif /* USE_TIMER_THREAD */
845
846 static isc_boolean_t
847 sooner(void *v1, void *v2) {
848         isc__timer_t *t1, *t2;
849
850         t1 = v1;
851         t2 = v2;
852         REQUIRE(VALID_TIMER(t1));
853         REQUIRE(VALID_TIMER(t2));
854
855         if (isc_time_compare(&t1->due, &t2->due) < 0)
856                 return (ISC_TRUE);
857         return (ISC_FALSE);
858 }
859
860 static void
861 set_index(void *what, unsigned int index) {
862         isc__timer_t *timer;
863
864         timer = what;
865         REQUIRE(VALID_TIMER(timer));
866
867         timer->index = index;
868 }
869
870 ISC_TIMERFUNC_SCOPE isc_result_t
871 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
872         isc__timermgr_t *manager;
873         isc_result_t result;
874
875         /*
876          * Create a timer manager.
877          */
878
879         REQUIRE(managerp != NULL && *managerp == NULL);
880
881 #ifdef USE_SHARED_MANAGER
882         if (timermgr != NULL) {
883                 timermgr->refs++;
884                 *managerp = (isc_timermgr_t *)timermgr;
885                 return (ISC_R_SUCCESS);
886         }
887 #endif /* USE_SHARED_MANAGER */
888
889         manager = isc_mem_get(mctx, sizeof(*manager));
890         if (manager == NULL)
891                 return (ISC_R_NOMEMORY);
892
893         manager->common.impmagic = TIMER_MANAGER_MAGIC;
894         manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
895         manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
896         manager->mctx = NULL;
897         manager->done = ISC_FALSE;
898         INIT_LIST(manager->timers);
899         manager->nscheduled = 0;
900         isc_time_settoepoch(&manager->due);
901         manager->heap = NULL;
902         result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
903         if (result != ISC_R_SUCCESS) {
904                 INSIST(result == ISC_R_NOMEMORY);
905                 isc_mem_put(mctx, manager, sizeof(*manager));
906                 return (ISC_R_NOMEMORY);
907         }
908         result = isc_mutex_init(&manager->lock);
909         if (result != ISC_R_SUCCESS) {
910                 isc_heap_destroy(&manager->heap);
911                 isc_mem_put(mctx, manager, sizeof(*manager));
912                 return (result);
913         }
914         isc_mem_attach(mctx, &manager->mctx);
915 #ifdef USE_TIMER_THREAD
916         if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
917                 isc_mem_detach(&manager->mctx);
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_condition_init() %s",
923                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
924                                                 ISC_MSG_FAILED, "failed"));
925                 return (ISC_R_UNEXPECTED);
926         }
927         if (isc_thread_create(run, manager, &manager->thread) !=
928             ISC_R_SUCCESS) {
929                 isc_mem_detach(&manager->mctx);
930                 (void)isc_condition_destroy(&manager->wakeup);
931                 DESTROYLOCK(&manager->lock);
932                 isc_heap_destroy(&manager->heap);
933                 isc_mem_put(mctx, manager, sizeof(*manager));
934                 UNEXPECTED_ERROR(__FILE__, __LINE__,
935                                  "isc_thread_create() %s",
936                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
937                                                 ISC_MSG_FAILED, "failed"));
938                 return (ISC_R_UNEXPECTED);
939         }
940 #endif
941 #ifdef USE_SHARED_MANAGER
942         manager->refs = 1;
943         timermgr = manager;
944 #endif /* USE_SHARED_MANAGER */
945
946         *managerp = (isc_timermgr_t *)manager;
947
948         return (ISC_R_SUCCESS);
949 }
950
951 ISC_TIMERFUNC_SCOPE void
952 isc__timermgr_poke(isc_timermgr_t *manager0) {
953 #ifdef USE_TIMER_THREAD
954         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
955
956         REQUIRE(VALID_MANAGER(manager));
957
958         SIGNAL(&manager->wakeup);
959 #else
960         UNUSED(manager0);
961 #endif
962 }
963
964 ISC_TIMERFUNC_SCOPE void
965 isc__timermgr_destroy(isc_timermgr_t **managerp) {
966         isc__timermgr_t *manager;
967         isc_mem_t *mctx;
968
969         /*
970          * Destroy a timer manager.
971          */
972
973         REQUIRE(managerp != NULL);
974         manager = (isc__timermgr_t *)*managerp;
975         REQUIRE(VALID_MANAGER(manager));
976
977         LOCK(&manager->lock);
978
979 #ifdef USE_SHARED_MANAGER
980         manager->refs--;
981         if (manager->refs > 0) {
982                 UNLOCK(&manager->lock);
983                 *managerp = NULL;
984                 return;
985         }
986         timermgr = NULL;
987 #endif /* USE_SHARED_MANAGER */
988
989 #ifndef USE_TIMER_THREAD
990         isc__timermgr_dispatch((isc_timermgr_t *)manager);
991 #endif
992
993         REQUIRE(EMPTY(manager->timers));
994         manager->done = ISC_TRUE;
995
996 #ifdef USE_TIMER_THREAD
997         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
998                               ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
999         SIGNAL(&manager->wakeup);
1000 #endif /* USE_TIMER_THREAD */
1001
1002         UNLOCK(&manager->lock);
1003
1004 #ifdef USE_TIMER_THREAD
1005         /*
1006          * Wait for thread to exit.
1007          */
1008         if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
1009                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1010                                  "isc_thread_join() %s",
1011                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1012                                                 ISC_MSG_FAILED, "failed"));
1013 #endif /* USE_TIMER_THREAD */
1014
1015         /*
1016          * Clean up.
1017          */
1018 #ifdef USE_TIMER_THREAD
1019         (void)isc_condition_destroy(&manager->wakeup);
1020 #endif /* USE_TIMER_THREAD */
1021         DESTROYLOCK(&manager->lock);
1022         isc_heap_destroy(&manager->heap);
1023         manager->common.impmagic = 0;
1024         manager->common.magic = 0;
1025         mctx = manager->mctx;
1026         isc_mem_put(mctx, manager, sizeof(*manager));
1027         isc_mem_detach(&mctx);
1028
1029         *managerp = NULL;
1030
1031 #ifdef USE_SHARED_MANAGER
1032         timermgr = NULL;
1033 #endif
1034 }
1035
1036 #ifndef USE_TIMER_THREAD
1037 isc_result_t
1038 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
1039         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1040
1041 #ifdef USE_SHARED_MANAGER
1042         if (manager == NULL)
1043                 manager = timermgr;
1044 #endif
1045         if (manager == NULL || manager->nscheduled == 0)
1046                 return (ISC_R_NOTFOUND);
1047         *when = manager->due;
1048         return (ISC_R_SUCCESS);
1049 }
1050
1051 void
1052 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
1053         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1054         isc_time_t now;
1055
1056 #ifdef USE_SHARED_MANAGER
1057         if (manager == NULL)
1058                 manager = timermgr;
1059 #endif
1060         if (manager == NULL)
1061                 return;
1062         TIME_NOW(&now);
1063         dispatch(manager, &now);
1064 }
1065 #endif /* USE_TIMER_THREAD */
1066
1067 #ifdef USE_TIMERIMPREGISTER
1068 isc_result_t
1069 isc__timer_register() {
1070         return (isc_timer_register(isc__timermgr_create));
1071 }
1072 #endif