Merge branch 'vendor/SENDMAIL'
[dragonfly.git] / contrib / bind / lib / isc / timer.c
1 /*
2  * Copyright (C) 2004, 2005, 2007-2009  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.81.32.7 2009/01/23 23:46:44 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         isc_boolean_t need_wakeup = ISC_FALSE;
247         isc_timermgr_t *manager;
248
249         /*
250          * The caller must ensure locking.
251          */
252
253         manager = timer->manager;
254         if (timer->index > 0) {
255                 if (timer->index == 1)
256                         need_wakeup = ISC_TRUE;
257                 isc_heap_delete(manager->heap, timer->index);
258                 timer->index = 0;
259                 INSIST(manager->nscheduled > 0);
260                 manager->nscheduled--;
261 #ifdef ISC_PLATFORM_USETHREADS
262                 if (need_wakeup) {
263                         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
264                                               ISC_MSG_SIGNALDESCHED,
265                                               "signal (deschedule)"));
266                         SIGNAL(&manager->wakeup);
267                 }
268 #endif /* ISC_PLATFORM_USETHREADS */
269         }
270 }
271
272 static void
273 destroy(isc_timer_t *timer) {
274         isc_timermgr_t *manager = timer->manager;
275
276         /*
277          * The caller must ensure it is safe to destroy the timer.
278          */
279
280         LOCK(&manager->lock);
281
282         (void)isc_task_purgerange(timer->task,
283                                   timer,
284                                   ISC_TIMEREVENT_FIRSTEVENT,
285                                   ISC_TIMEREVENT_LASTEVENT,
286                                   NULL);
287         deschedule(timer);
288         UNLINK(manager->timers, timer, link);
289
290         UNLOCK(&manager->lock);
291
292         isc_task_detach(&timer->task);
293         DESTROYLOCK(&timer->lock);
294         timer->magic = 0;
295         isc_mem_put(manager->mctx, timer, sizeof(*timer));
296 }
297
298 isc_result_t
299 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
300                  isc_time_t *expires, isc_interval_t *interval,
301                  isc_task_t *task, isc_taskaction_t action, const void *arg,
302                  isc_timer_t **timerp)
303 {
304         isc_timer_t *timer;
305         isc_result_t result;
306         isc_time_t now;
307
308         /*
309          * Create a new 'type' timer managed by 'manager'.  The timers
310          * parameters are specified by 'expires' and 'interval'.  Events
311          * will be posted to 'task' and when dispatched 'action' will be
312          * called with 'arg' as the arg value.  The new timer is returned
313          * in 'timerp'.
314          */
315
316         REQUIRE(VALID_MANAGER(manager));
317         REQUIRE(task != NULL);
318         REQUIRE(action != NULL);
319         if (expires == NULL)
320                 expires = isc_time_epoch;
321         if (interval == NULL)
322                 interval = isc_interval_zero;
323         REQUIRE(type == isc_timertype_inactive ||
324                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
325         REQUIRE(timerp != NULL && *timerp == NULL);
326         REQUIRE(type != isc_timertype_limited ||
327                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
328
329         /*
330          * Get current time.
331          */
332         if (type != isc_timertype_inactive) {
333                 TIME_NOW(&now);
334         } else {
335                 /*
336                  * We don't have to do this, but it keeps the compiler from
337                  * complaining about "now" possibly being used without being
338                  * set, even though it will never actually happen.
339                  */
340                 isc_time_settoepoch(&now);
341         }
342
343
344         timer = isc_mem_get(manager->mctx, sizeof(*timer));
345         if (timer == NULL)
346                 return (ISC_R_NOMEMORY);
347
348         timer->manager = manager;
349         timer->references = 1;
350
351         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
352                 result = isc_time_add(&now, interval, &timer->idle);
353                 if (result != ISC_R_SUCCESS) {
354                         isc_mem_put(manager->mctx, timer, sizeof(*timer));
355                         return (result);
356                 }
357         } else
358                 isc_time_settoepoch(&timer->idle);
359
360         timer->type = type;
361         timer->expires = *expires;
362         timer->interval = *interval;
363         timer->task = NULL;
364         isc_task_attach(task, &timer->task);
365         timer->action = action;
366         /*
367          * Removing the const attribute from "arg" is the best of two
368          * evils here.  If the timer->arg member is made const, then
369          * it affects a great many recipients of the timer event
370          * which did not pass in an "arg" that was truly const.
371          * Changing isc_timer_create() to not have "arg" prototyped as const,
372          * though, can cause compilers warnings for calls that *do*
373          * have a truly const arg.  The caller will have to carefully
374          * keep track of whether arg started as a true const.
375          */
376         DE_CONST(arg, timer->arg);
377         timer->index = 0;
378         result = isc_mutex_init(&timer->lock);
379         if (result != ISC_R_SUCCESS) {
380                 isc_task_detach(&timer->task);
381                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
382                 return (result);
383         }
384         ISC_LINK_INIT(timer, link);
385         timer->magic = TIMER_MAGIC;
386
387         LOCK(&manager->lock);
388
389         /*
390          * Note we don't have to lock the timer like we normally would because
391          * there are no external references to it yet.
392          */
393
394         if (type != isc_timertype_inactive)
395                 result = schedule(timer, &now, ISC_TRUE);
396         else
397                 result = ISC_R_SUCCESS;
398         if (result == ISC_R_SUCCESS)
399                 APPEND(manager->timers, timer, link);
400
401         UNLOCK(&manager->lock);
402
403         if (result != ISC_R_SUCCESS) {
404                 timer->magic = 0;
405                 DESTROYLOCK(&timer->lock);
406                 isc_task_detach(&timer->task);
407                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
408                 return (result);
409         }
410
411         *timerp = timer;
412
413         return (ISC_R_SUCCESS);
414 }
415
416 isc_result_t
417 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
418                 isc_time_t *expires, isc_interval_t *interval,
419                 isc_boolean_t purge)
420 {
421         isc_time_t now;
422         isc_timermgr_t *manager;
423         isc_result_t result;
424
425         /*
426          * Change the timer's type, expires, and interval values to the given
427          * values.  If 'purge' is ISC_TRUE, any pending events from this timer
428          * are purged from its task's event queue.
429          */
430
431         REQUIRE(VALID_TIMER(timer));
432         manager = timer->manager;
433         REQUIRE(VALID_MANAGER(manager));
434         if (expires == NULL)
435                 expires = isc_time_epoch;
436         if (interval == NULL)
437                 interval = isc_interval_zero;
438         REQUIRE(type == isc_timertype_inactive ||
439                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
440         REQUIRE(type != isc_timertype_limited ||
441                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
442
443         /*
444          * Get current time.
445          */
446         if (type != isc_timertype_inactive) {
447                 TIME_NOW(&now);
448         } else {
449                 /*
450                  * We don't have to do this, but it keeps the compiler from
451                  * complaining about "now" possibly being used without being
452                  * set, even though it will never actually happen.
453                  */
454                 isc_time_settoepoch(&now);
455         }
456
457         manager = timer->manager;
458
459         LOCK(&manager->lock);
460         LOCK(&timer->lock);
461
462         if (purge)
463                 (void)isc_task_purgerange(timer->task,
464                                           timer,
465                                           ISC_TIMEREVENT_FIRSTEVENT,
466                                           ISC_TIMEREVENT_LASTEVENT,
467                                           NULL);
468         timer->type = type;
469         timer->expires = *expires;
470         timer->interval = *interval;
471         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
472                 result = isc_time_add(&now, interval, &timer->idle);
473         } else {
474                 isc_time_settoepoch(&timer->idle);
475                 result = ISC_R_SUCCESS;
476         }
477
478         if (result == ISC_R_SUCCESS) {
479                 if (type == isc_timertype_inactive) {
480                         deschedule(timer);
481                         result = ISC_R_SUCCESS;
482                 } else
483                         result = schedule(timer, &now, ISC_TRUE);
484         }
485
486         UNLOCK(&timer->lock);
487         UNLOCK(&manager->lock);
488
489         return (result);
490 }
491
492 isc_timertype_t
493 isc_timer_gettype(isc_timer_t *timer) {
494         isc_timertype_t t;
495
496         REQUIRE(VALID_TIMER(timer));
497
498         LOCK(&timer->lock);
499         t = timer->type;
500         UNLOCK(&timer->lock);
501
502         return (t);
503 }
504
505 isc_result_t
506 isc_timer_touch(isc_timer_t *timer) {
507         isc_result_t result;
508         isc_time_t now;
509
510         /*
511          * Set the last-touched time of 'timer' to the current time.
512          */
513
514         REQUIRE(VALID_TIMER(timer));
515
516         LOCK(&timer->lock);
517
518         /*
519          * We'd like to
520          *
521          *      REQUIRE(timer->type == isc_timertype_once);
522          *
523          * but we cannot without locking the manager lock too, which we
524          * don't want to do.
525          */
526
527         TIME_NOW(&now);
528         result = isc_time_add(&now, &timer->interval, &timer->idle);
529
530         UNLOCK(&timer->lock);
531
532         return (result);
533 }
534
535 void
536 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
537         /*
538          * Attach *timerp to timer.
539          */
540
541         REQUIRE(VALID_TIMER(timer));
542         REQUIRE(timerp != NULL && *timerp == NULL);
543
544         LOCK(&timer->lock);
545         timer->references++;
546         UNLOCK(&timer->lock);
547
548         *timerp = timer;
549 }
550
551 void
552 isc_timer_detach(isc_timer_t **timerp) {
553         isc_timer_t *timer;
554         isc_boolean_t free_timer = ISC_FALSE;
555
556         /*
557          * Detach *timerp from its timer.
558          */
559
560         REQUIRE(timerp != NULL);
561         timer = *timerp;
562         REQUIRE(VALID_TIMER(timer));
563
564         LOCK(&timer->lock);
565         REQUIRE(timer->references > 0);
566         timer->references--;
567         if (timer->references == 0)
568                 free_timer = ISC_TRUE;
569         UNLOCK(&timer->lock);
570
571         if (free_timer)
572                 destroy(timer);
573
574         *timerp = NULL;
575 }
576
577 static void
578 dispatch(isc_timermgr_t *manager, isc_time_t *now) {
579         isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
580         isc_timerevent_t *event;
581         isc_eventtype_t type = 0;
582         isc_timer_t *timer;
583         isc_result_t result;
584         isc_boolean_t idle;
585
586         /*!
587          * The caller must be holding the manager lock.
588          */
589
590         while (manager->nscheduled > 0 && !done) {
591                 timer = isc_heap_element(manager->heap, 1);
592                 INSIST(timer->type != isc_timertype_inactive);
593                 if (isc_time_compare(now, &timer->due) >= 0) {
594                         if (timer->type == isc_timertype_ticker) {
595                                 type = ISC_TIMEREVENT_TICK;
596                                 post_event = ISC_TRUE;
597                                 need_schedule = ISC_TRUE;
598                         } else if (timer->type == isc_timertype_limited) {
599                                 int cmp;
600                                 cmp = isc_time_compare(now, &timer->expires);
601                                 if (cmp >= 0) {
602                                         type = ISC_TIMEREVENT_LIFE;
603                                         post_event = ISC_TRUE;
604                                         need_schedule = ISC_FALSE;
605                                 } else {
606                                         type = ISC_TIMEREVENT_TICK;
607                                         post_event = ISC_TRUE;
608                                         need_schedule = ISC_TRUE;
609                                 }
610                         } else if (!isc_time_isepoch(&timer->expires) &&
611                                    isc_time_compare(now,
612                                                     &timer->expires) >= 0) {
613                                 type = ISC_TIMEREVENT_LIFE;
614                                 post_event = ISC_TRUE;
615                                 need_schedule = ISC_FALSE;
616                         } else {
617                                 idle = ISC_FALSE;
618
619                                 LOCK(&timer->lock);
620                                 if (!isc_time_isepoch(&timer->idle) &&
621                                     isc_time_compare(now,
622                                                      &timer->idle) >= 0) {
623                                         idle = ISC_TRUE;
624                                 }
625                                 UNLOCK(&timer->lock);
626                                 if (idle) {
627                                         type = ISC_TIMEREVENT_IDLE;
628                                         post_event = ISC_TRUE;
629                                         need_schedule = ISC_FALSE;
630                                 } else {
631                                         /*
632                                          * Idle timer has been touched;
633                                          * reschedule.
634                                          */
635                                         XTRACEID(isc_msgcat_get(isc_msgcat,
636                                                                 ISC_MSGSET_TIMER,
637                                                                 ISC_MSG_IDLERESCHED,
638                                                                 "idle reschedule"),
639                                                  timer);
640                                         post_event = ISC_FALSE;
641                                         need_schedule = ISC_TRUE;
642                                 }
643                         }
644
645                         if (post_event) {
646                                 XTRACEID(isc_msgcat_get(isc_msgcat,
647                                                         ISC_MSGSET_TIMER,
648                                                         ISC_MSG_POSTING,
649                                                         "posting"), timer);
650                                 /*
651                                  * XXX We could preallocate this event.
652                                  */
653                                 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
654                                                            timer,
655                                                            type,
656                                                            timer->action,
657                                                            timer->arg,
658                                                            sizeof(*event));
659
660                                 if (event != NULL) {
661                                         event->due = timer->due;
662                                         isc_task_send(timer->task,
663                                                       ISC_EVENT_PTR(&event));
664                                 } else
665                                         UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
666                                                  isc_msgcat_get(isc_msgcat,
667                                                          ISC_MSGSET_TIMER,
668                                                          ISC_MSG_EVENTNOTALLOC,
669                                                          "couldn't "
670                                                          "allocate event"));
671                         }
672
673                         timer->index = 0;
674                         isc_heap_delete(manager->heap, 1);
675                         manager->nscheduled--;
676
677                         if (need_schedule) {
678                                 result = schedule(timer, now, ISC_FALSE);
679                                 if (result != ISC_R_SUCCESS)
680                                         UNEXPECTED_ERROR(__FILE__, __LINE__,
681                                                          "%s: %u",
682                                                 isc_msgcat_get(isc_msgcat,
683                                                         ISC_MSGSET_TIMER,
684                                                         ISC_MSG_SCHEDFAIL,
685                                                         "couldn't schedule "
686                                                         "timer"),
687                                                          result);
688                         }
689                 } else {
690                         manager->due = timer->due;
691                         done = ISC_TRUE;
692                 }
693         }
694 }
695
696 #ifdef ISC_PLATFORM_USETHREADS
697 static isc_threadresult_t
698 #ifdef _WIN32                   /* XXXDCL */
699 WINAPI
700 #endif
701 run(void *uap) {
702         isc_timermgr_t *manager = uap;
703         isc_time_t now;
704         isc_result_t result;
705
706         LOCK(&manager->lock);
707         while (!manager->done) {
708                 TIME_NOW(&now);
709
710                 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
711                                           ISC_MSG_RUNNING,
712                                           "running"), now);
713
714                 dispatch(manager, &now);
715
716                 if (manager->nscheduled > 0) {
717                         XTRACETIME2(isc_msgcat_get(isc_msgcat,
718                                                    ISC_MSGSET_GENERAL,
719                                                    ISC_MSG_WAITUNTIL,
720                                                    "waituntil"),
721                                     manager->due, now);
722                         result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
723                         INSIST(result == ISC_R_SUCCESS ||
724                                result == ISC_R_TIMEDOUT);
725                 } else {
726                         XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
727                                                   ISC_MSG_WAIT, "wait"), now);
728                         WAIT(&manager->wakeup, &manager->lock);
729                 }
730                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
731                                       ISC_MSG_WAKEUP, "wakeup"));
732         }
733         UNLOCK(&manager->lock);
734
735         return ((isc_threadresult_t)0);
736 }
737 #endif /* ISC_PLATFORM_USETHREADS */
738
739 static isc_boolean_t
740 sooner(void *v1, void *v2) {
741         isc_timer_t *t1, *t2;
742
743         t1 = v1;
744         t2 = v2;
745         REQUIRE(VALID_TIMER(t1));
746         REQUIRE(VALID_TIMER(t2));
747
748         if (isc_time_compare(&t1->due, &t2->due) < 0)
749                 return (ISC_TRUE);
750         return (ISC_FALSE);
751 }
752
753 static void
754 set_index(void *what, unsigned int index) {
755         isc_timer_t *timer;
756
757         timer = what;
758         REQUIRE(VALID_TIMER(timer));
759
760         timer->index = index;
761 }
762
763 isc_result_t
764 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
765         isc_timermgr_t *manager;
766         isc_result_t result;
767
768         /*
769          * Create a timer manager.
770          */
771
772         REQUIRE(managerp != NULL && *managerp == NULL);
773
774 #ifndef ISC_PLATFORM_USETHREADS
775         if (timermgr != NULL) {
776                 timermgr->refs++;
777                 *managerp = timermgr;
778                 return (ISC_R_SUCCESS);
779         }
780 #endif /* ISC_PLATFORM_USETHREADS */
781
782         manager = isc_mem_get(mctx, sizeof(*manager));
783         if (manager == NULL)
784                 return (ISC_R_NOMEMORY);
785
786         manager->magic = TIMER_MANAGER_MAGIC;
787         manager->mctx = NULL;
788         manager->done = ISC_FALSE;
789         INIT_LIST(manager->timers);
790         manager->nscheduled = 0;
791         isc_time_settoepoch(&manager->due);
792         manager->heap = NULL;
793         result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
794         if (result != ISC_R_SUCCESS) {
795                 INSIST(result == ISC_R_NOMEMORY);
796                 isc_mem_put(mctx, manager, sizeof(*manager));
797                 return (ISC_R_NOMEMORY);
798         }
799         result = isc_mutex_init(&manager->lock);
800         if (result != ISC_R_SUCCESS) {
801                 isc_heap_destroy(&manager->heap);
802                 isc_mem_put(mctx, manager, sizeof(*manager));
803                 return (result);
804         }
805         isc_mem_attach(mctx, &manager->mctx);
806 #ifdef ISC_PLATFORM_USETHREADS
807         if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
808                 isc_mem_detach(&manager->mctx);
809                 DESTROYLOCK(&manager->lock);
810                 isc_heap_destroy(&manager->heap);
811                 isc_mem_put(mctx, manager, sizeof(*manager));
812                 UNEXPECTED_ERROR(__FILE__, __LINE__,
813                                  "isc_condition_init() %s",
814                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
815                                                 ISC_MSG_FAILED, "failed"));
816                 return (ISC_R_UNEXPECTED);
817         }
818         if (isc_thread_create(run, manager, &manager->thread) !=
819             ISC_R_SUCCESS) {
820                 isc_mem_detach(&manager->mctx);
821                 (void)isc_condition_destroy(&manager->wakeup);
822                 DESTROYLOCK(&manager->lock);
823                 isc_heap_destroy(&manager->heap);
824                 isc_mem_put(mctx, manager, sizeof(*manager));
825                 UNEXPECTED_ERROR(__FILE__, __LINE__,
826                                  "isc_thread_create() %s",
827                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
828                                                 ISC_MSG_FAILED, "failed"));
829                 return (ISC_R_UNEXPECTED);
830         }
831 #else /* ISC_PLATFORM_USETHREADS */
832         manager->refs = 1;
833         timermgr = manager;
834 #endif /* ISC_PLATFORM_USETHREADS */
835
836         *managerp = manager;
837
838         return (ISC_R_SUCCESS);
839 }
840
841 void
842 isc_timermgr_poke(isc_timermgr_t *manager) {
843 #ifdef ISC_PLATFORM_USETHREADS
844         REQUIRE(VALID_MANAGER(manager));
845
846         SIGNAL(&manager->wakeup);
847 #else
848         UNUSED(manager);
849 #endif
850 }
851
852 void
853 isc_timermgr_destroy(isc_timermgr_t **managerp) {
854         isc_timermgr_t *manager;
855         isc_mem_t *mctx;
856
857         /*
858          * Destroy a timer manager.
859          */
860
861         REQUIRE(managerp != NULL);
862         manager = *managerp;
863         REQUIRE(VALID_MANAGER(manager));
864
865         LOCK(&manager->lock);
866
867 #ifndef ISC_PLATFORM_USETHREADS
868         if (manager->refs > 1) {
869                 manager->refs--;
870                 UNLOCK(&manager->lock);
871                 *managerp = NULL;
872                 return;
873         }
874
875         isc__timermgr_dispatch();
876 #endif /* ISC_PLATFORM_USETHREADS */
877
878         REQUIRE(EMPTY(manager->timers));
879         manager->done = ISC_TRUE;
880
881 #ifdef ISC_PLATFORM_USETHREADS
882         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
883                               ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
884         SIGNAL(&manager->wakeup);
885 #endif /* ISC_PLATFORM_USETHREADS */
886
887         UNLOCK(&manager->lock);
888
889 #ifdef ISC_PLATFORM_USETHREADS
890         /*
891          * Wait for thread to exit.
892          */
893         if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
894                 UNEXPECTED_ERROR(__FILE__, __LINE__,
895                                  "isc_thread_join() %s",
896                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
897                                                 ISC_MSG_FAILED, "failed"));
898 #endif /* ISC_PLATFORM_USETHREADS */
899
900         /*
901          * Clean up.
902          */
903 #ifdef ISC_PLATFORM_USETHREADS
904         (void)isc_condition_destroy(&manager->wakeup);
905 #endif /* ISC_PLATFORM_USETHREADS */
906         DESTROYLOCK(&manager->lock);
907         isc_heap_destroy(&manager->heap);
908         manager->magic = 0;
909         mctx = manager->mctx;
910         isc_mem_put(mctx, manager, sizeof(*manager));
911         isc_mem_detach(&mctx);
912
913         *managerp = NULL;
914 }
915
916 #ifndef ISC_PLATFORM_USETHREADS
917 isc_result_t
918 isc__timermgr_nextevent(isc_time_t *when) {
919         if (timermgr == NULL || timermgr->nscheduled == 0)
920                 return (ISC_R_NOTFOUND);
921         *when = timermgr->due;
922         return (ISC_R_SUCCESS);
923 }
924
925 void
926 isc__timermgr_dispatch(void) {
927         isc_time_t now;
928         if (timermgr == NULL)
929                 return;
930         TIME_NOW(&now);
931         dispatch(timermgr, &now);
932 }
933 #endif /* ISC_PLATFORM_USETHREADS */