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