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