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