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