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