1 /*
2 * Copyright (C) 2004, 2005, 2007-2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2002 Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /* $Id$ */
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 #ifdef OPENSSL_LEAKS
38 #include <openssl/err.h>
39 #endif
40
41 /* See task.c about the following definition: */
42 #ifdef BIND9
43 #ifdef ISC_PLATFORM_USETHREADS
44 #define USE_TIMER_THREAD
45 #else
46 #define USE_SHARED_MANAGER
47 #endif /* ISC_PLATFORM_USETHREADS */
48 #endif /* BIND9 */
49
50 #ifndef USE_TIMER_THREAD
51 #include "timer_p.h"
52 #endif /* USE_TIMER_THREAD */
53
54 #ifdef ISC_TIMER_TRACE
55 #define XTRACE(s) fprintf(stderr, "%s\n", (s))
56 #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
57 #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \
58 (d).seconds, (d).nanoseconds)
59 #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
60 (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
61 #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
62 (d).seconds, (d).nanoseconds)
63 #else
64 #define XTRACE(s)
65 #define XTRACEID(s, t)
66 #define XTRACETIME(s, d)
67 #define XTRACETIME2(s, d, n)
68 #define XTRACETIMER(s, t, d)
69 #endif /* ISC_TIMER_TRACE */
70
71 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
72 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
73
74 typedef struct isc__timer isc__timer_t;
75 typedef struct isc__timermgr isc__timermgr_t;
76
77 struct isc__timer {
78 /*! Not locked. */
79 isc_timer_t common;
80 isc__timermgr_t * manager;
81 isc_mutex_t lock;
82 /*! Locked by timer lock. */
83 unsigned int references;
84 isc_time_t idle;
85 /*! Locked by manager lock. */
86 isc_timertype_t type;
87 isc_time_t expires;
88 isc_interval_t interval;
89 isc_task_t * task;
90 isc_taskaction_t action;
91 void * arg;
92 unsigned int index;
93 isc_time_t due;
94 LINK(isc__timer_t) link;
95 };
96
97 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
98 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
99
100 struct isc__timermgr {
101 /* Not locked. */
102 isc_timermgr_t common;
103 isc_mem_t * mctx;
104 isc_mutex_t lock;
105 /* Locked by manager lock. */
106 isc_boolean_t done;
107 LIST(isc__timer_t) timers;
108 unsigned int nscheduled;
109 isc_time_t due;
110 #ifdef USE_TIMER_THREAD
111 isc_condition_t wakeup;
112 isc_thread_t thread;
113 #endif /* USE_TIMER_THREAD */
114 #ifdef USE_SHARED_MANAGER
115 unsigned int refs;
116 #endif /* USE_SHARED_MANAGER */
117 isc_heap_t * heap;
118 };
119
120 /*%
121 * The followings can be either static or public, depending on build
122 * environment.
123 */
124
125 #ifdef BIND9
126 #define ISC_TIMERFUNC_SCOPE
127 #else
128 #define ISC_TIMERFUNC_SCOPE static
129 #endif
130
131 ISC_TIMERFUNC_SCOPE isc_result_t
132 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
133 isc_time_t *expires, isc_interval_t *interval,
134 isc_task_t *task, isc_taskaction_t action, const void *arg,
135 isc_timer_t **timerp);
136 ISC_TIMERFUNC_SCOPE isc_result_t
137 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
138 isc_time_t *expires, isc_interval_t *interval,
139 isc_boolean_t purge);
140 ISC_TIMERFUNC_SCOPE isc_timertype_t
141 isc__timer_gettype(isc_timer_t *timer);
142 ISC_TIMERFUNC_SCOPE isc_result_t
143 isc__timer_touch(isc_timer_t *timer);
144 ISC_TIMERFUNC_SCOPE void
145 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
146 ISC_TIMERFUNC_SCOPE void
147 isc__timer_detach(isc_timer_t **timerp);
148 ISC_TIMERFUNC_SCOPE isc_result_t
149 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
150 ISC_TIMERFUNC_SCOPE void
151 isc__timermgr_poke(isc_timermgr_t *manager0);
152 ISC_TIMERFUNC_SCOPE void
153 isc__timermgr_destroy(isc_timermgr_t **managerp);
154
155 static struct isc__timermethods {
156 isc_timermethods_t methods;
157
158 /*%
159 * The following are defined just for avoiding unused static functions.
160 */
161 #ifndef BIND9
162 void *gettype;
163 #endif
164 } timermethods = {
165 {
166 isc__timer_attach,
167 isc__timer_detach,
168 isc__timer_reset,
169 isc__timer_touch
170 }
171 #ifndef BIND9
172 ,
173 (void *)isc__timer_gettype
174 #endif
175 };
176
177 static struct isc__timermgrmethods {
178 isc_timermgrmethods_t methods;
179 #ifndef BIND9
180 void *poke; /* see above */
181 #endif
182 } timermgrmethods = {
183 {
184 isc__timermgr_destroy,
185 isc__timer_create
186 }
187 #ifndef BIND9
188 ,
189 (void *)isc__timermgr_poke
190 #endif
191 };
192
193 #ifdef USE_SHARED_MANAGER
194 /*!
195 * If the manager is supposed to be shared, there can be only one.
196 */
197 static isc__timermgr_t *timermgr = NULL;
198 #endif /* USE_SHARED_MANAGER */
199
200 static inline isc_result_t
schedule(isc__timer_t * timer,isc_time_t * now,isc_boolean_t signal_ok)201 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
202 isc_result_t result;
203 isc__timermgr_t *manager;
204 isc_time_t due;
205 int cmp;
206 #ifdef USE_TIMER_THREAD
207 isc_boolean_t timedwait;
208 #endif
209
210 /*!
211 * Note: the caller must ensure locking.
212 */
213
214 REQUIRE(timer->type != isc_timertype_inactive);
215
216 #ifndef USE_TIMER_THREAD
217 UNUSED(signal_ok);
218 #endif /* USE_TIMER_THREAD */
219
220 manager = timer->manager;
221
222 #ifdef USE_TIMER_THREAD
223 /*!
224 * If the manager was timed wait, we may need to signal the
225 * manager to force a wakeup.
226 */
227 timedwait = ISC_TF(manager->nscheduled > 0 &&
228 isc_time_seconds(&manager->due) != 0);
229 #endif
230
231 /*
232 * Compute the new due time.
233 */
234 if (timer->type != isc_timertype_once) {
235 result = isc_time_add(now, &timer->interval, &due);
236 if (result != ISC_R_SUCCESS)
237 return (result);
238 if (timer->type == isc_timertype_limited &&
239 isc_time_compare(&timer->expires, &due) < 0)
240 due = timer->expires;
241 } else {
242 if (isc_time_isepoch(&timer->idle))
243 due = timer->expires;
244 else if (isc_time_isepoch(&timer->expires))
245 due = timer->idle;
246 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
247 due = timer->idle;
248 else
249 due = timer->expires;
250 }
251
252 /*
253 * Schedule the timer.
254 */
255
256 if (timer->index > 0) {
257 /*
258 * Already scheduled.
259 */
260 cmp = isc_time_compare(&due, &timer->due);
261 timer->due = due;
262 switch (cmp) {
263 case -1:
264 isc_heap_increased(manager->heap, timer->index);
265 break;
266 case 1:
267 isc_heap_decreased(manager->heap, timer->index);
268 break;
269 case 0:
270 /* Nothing to do. */
271 break;
272 }
273 } else {
274 timer->due = due;
275 result = isc_heap_insert(manager->heap, timer);
276 if (result != ISC_R_SUCCESS) {
277 INSIST(result == ISC_R_NOMEMORY);
278 return (ISC_R_NOMEMORY);
279 }
280 manager->nscheduled++;
281 }
282
283 XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
284 ISC_MSG_SCHEDULE, "schedule"), timer, due);
285
286 /*
287 * If this timer is at the head of the queue, we need to ensure
288 * that we won't miss it if it has a more recent due time than
289 * the current "next" timer. We do this either by waking up the
290 * run thread, or explicitly setting the value in the manager.
291 */
292 #ifdef USE_TIMER_THREAD
293
294 /*
295 * This is a temporary (probably) hack to fix a bug on tru64 5.1
296 * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
297 * return when the time expires, so here, we check to see if
298 * we're 15 seconds or more behind, and if we are, we signal
299 * the dispatcher. This isn't such a bad idea as a general purpose
300 * watchdog, so perhaps we should just leave it in here.
301 */
302 if (signal_ok && timedwait) {
303 isc_interval_t fifteen;
304 isc_time_t then;
305
306 isc_interval_set(&fifteen, 15, 0);
307 result = isc_time_add(&manager->due, &fifteen, &then);
308
309 if (result == ISC_R_SUCCESS &&
310 isc_time_compare(&then, now) < 0) {
311 SIGNAL(&manager->wakeup);
312 signal_ok = ISC_FALSE;
313 isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
314 ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
315 "*** POKED TIMER ***");
316 }
317 }
318
319 if (timer->index == 1 && signal_ok) {
320 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
321 ISC_MSG_SIGNALSCHED,
322 "signal (schedule)"));
323 SIGNAL(&manager->wakeup);
324 }
325 #else /* USE_TIMER_THREAD */
326 if (timer->index == 1 &&
327 isc_time_compare(&timer->due, &manager->due) < 0)
328 manager->due = timer->due;
329 #endif /* USE_TIMER_THREAD */
330
331 return (ISC_R_SUCCESS);
332 }
333
334 static inline void
deschedule(isc__timer_t * timer)335 deschedule(isc__timer_t *timer) {
336 #ifdef USE_TIMER_THREAD
337 isc_boolean_t need_wakeup = ISC_FALSE;
338 #endif
339 isc__timermgr_t *manager;
340
341 /*
342 * The caller must ensure locking.
343 */
344
345 manager = timer->manager;
346 if (timer->index > 0) {
347 #ifdef USE_TIMER_THREAD
348 if (timer->index == 1)
349 need_wakeup = ISC_TRUE;
350 #endif
351 isc_heap_delete(manager->heap, timer->index);
352 timer->index = 0;
353 INSIST(manager->nscheduled > 0);
354 manager->nscheduled--;
355 #ifdef USE_TIMER_THREAD
356 if (need_wakeup) {
357 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
358 ISC_MSG_SIGNALDESCHED,
359 "signal (deschedule)"));
360 SIGNAL(&manager->wakeup);
361 }
362 #endif /* USE_TIMER_THREAD */
363 }
364 }
365
366 static void
destroy(isc__timer_t * timer)367 destroy(isc__timer_t *timer) {
368 isc__timermgr_t *manager = timer->manager;
369
370 /*
371 * The caller must ensure it is safe to destroy the timer.
372 */
373
374 LOCK(&manager->lock);
375
376 (void)isc_task_purgerange(timer->task,
377 timer,
378 ISC_TIMEREVENT_FIRSTEVENT,
379 ISC_TIMEREVENT_LASTEVENT,
380 NULL);
381 deschedule(timer);
382 UNLINK(manager->timers, timer, link);
383
384 UNLOCK(&manager->lock);
385
386 isc_task_detach(&timer->task);
387 DESTROYLOCK(&timer->lock);
388 timer->common.impmagic = 0;
389 timer->common.magic = 0;
390 isc_mem_put(manager->mctx, timer, sizeof(*timer));
391 }
392
393 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_create(isc_timermgr_t * manager0,isc_timertype_t type,isc_time_t * expires,isc_interval_t * interval,isc_task_t * task,isc_taskaction_t action,const void * arg,isc_timer_t ** timerp)394 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
395 isc_time_t *expires, isc_interval_t *interval,
396 isc_task_t *task, isc_taskaction_t action, const void *arg,
397 isc_timer_t **timerp)
398 {
399 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
400 isc__timer_t *timer;
401 isc_result_t result;
402 isc_time_t now;
403
404 /*
405 * Create a new 'type' timer managed by 'manager'. The timers
406 * parameters are specified by 'expires' and 'interval'. Events
407 * will be posted to 'task' and when dispatched 'action' will be
408 * called with 'arg' as the arg value. The new timer is returned
409 * in 'timerp'.
410 */
411
412 REQUIRE(VALID_MANAGER(manager));
413 REQUIRE(task != NULL);
414 REQUIRE(action != NULL);
415 if (expires == NULL)
416 expires = isc_time_epoch;
417 if (interval == NULL)
418 interval = isc_interval_zero;
419 REQUIRE(type == isc_timertype_inactive ||
420 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
421 REQUIRE(timerp != NULL && *timerp == NULL);
422 REQUIRE(type != isc_timertype_limited ||
423 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
424
425 /*
426 * Get current time.
427 */
428 if (type != isc_timertype_inactive) {
429 TIME_NOW(&now);
430 } else {
431 /*
432 * We don't have to do this, but it keeps the compiler from
433 * complaining about "now" possibly being used without being
434 * set, even though it will never actually happen.
435 */
436 isc_time_settoepoch(&now);
437 }
438
439
440 timer = isc_mem_get(manager->mctx, sizeof(*timer));
441 if (timer == NULL)
442 return (ISC_R_NOMEMORY);
443
444 timer->manager = manager;
445 timer->references = 1;
446
447 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
448 result = isc_time_add(&now, interval, &timer->idle);
449 if (result != ISC_R_SUCCESS) {
450 isc_mem_put(manager->mctx, timer, sizeof(*timer));
451 return (result);
452 }
453 } else
454 isc_time_settoepoch(&timer->idle);
455
456 timer->type = type;
457 timer->expires = *expires;
458 timer->interval = *interval;
459 timer->task = NULL;
460 isc_task_attach(task, &timer->task);
461 timer->action = action;
462 /*
463 * Removing the const attribute from "arg" is the best of two
464 * evils here. If the timer->arg member is made const, then
465 * it affects a great many recipients of the timer event
466 * which did not pass in an "arg" that was truly const.
467 * Changing isc_timer_create() to not have "arg" prototyped as const,
468 * though, can cause compilers warnings for calls that *do*
469 * have a truly const arg. The caller will have to carefully
470 * keep track of whether arg started as a true const.
471 */
472 DE_CONST(arg, timer->arg);
473 timer->index = 0;
474 result = isc_mutex_init(&timer->lock);
475 if (result != ISC_R_SUCCESS) {
476 isc_task_detach(&timer->task);
477 isc_mem_put(manager->mctx, timer, sizeof(*timer));
478 return (result);
479 }
480 ISC_LINK_INIT(timer, link);
481 timer->common.impmagic = TIMER_MAGIC;
482 timer->common.magic = ISCAPI_TIMER_MAGIC;
483 timer->common.methods = (isc_timermethods_t *)&timermethods;
484
485 LOCK(&manager->lock);
486
487 /*
488 * Note we don't have to lock the timer like we normally would because
489 * there are no external references to it yet.
490 */
491
492 if (type != isc_timertype_inactive)
493 result = schedule(timer, &now, ISC_TRUE);
494 else
495 result = ISC_R_SUCCESS;
496 if (result == ISC_R_SUCCESS)
497 APPEND(manager->timers, timer, link);
498
499 UNLOCK(&manager->lock);
500
501 if (result != ISC_R_SUCCESS) {
502 timer->common.impmagic = 0;
503 timer->common.magic = 0;
504 DESTROYLOCK(&timer->lock);
505 isc_task_detach(&timer->task);
506 isc_mem_put(manager->mctx, timer, sizeof(*timer));
507 return (result);
508 }
509
510 *timerp = (isc_timer_t *)timer;
511
512 return (ISC_R_SUCCESS);
513 }
514
515 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_reset(isc_timer_t * timer0,isc_timertype_t type,isc_time_t * expires,isc_interval_t * interval,isc_boolean_t purge)516 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
517 isc_time_t *expires, isc_interval_t *interval,
518 isc_boolean_t purge)
519 {
520 isc__timer_t *timer = (isc__timer_t *)timer0;
521 isc_time_t now;
522 isc__timermgr_t *manager;
523 isc_result_t result;
524
525 /*
526 * Change the timer's type, expires, and interval values to the given
527 * values. If 'purge' is ISC_TRUE, any pending events from this timer
528 * are purged from its task's event queue.
529 */
530
531 REQUIRE(VALID_TIMER(timer));
532 manager = timer->manager;
533 REQUIRE(VALID_MANAGER(manager));
534
535 if (expires == NULL)
536 expires = isc_time_epoch;
537 if (interval == NULL)
538 interval = isc_interval_zero;
539 REQUIRE(type == isc_timertype_inactive ||
540 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
541 REQUIRE(type != isc_timertype_limited ||
542 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
543
544 /*
545 * Get current time.
546 */
547 if (type != isc_timertype_inactive) {
548 TIME_NOW(&now);
549 } else {
550 /*
551 * We don't have to do this, but it keeps the compiler from
552 * complaining about "now" possibly being used without being
553 * set, even though it will never actually happen.
554 */
555 isc_time_settoepoch(&now);
556 }
557
558 LOCK(&manager->lock);
559 LOCK(&timer->lock);
560
561 if (purge)
562 (void)isc_task_purgerange(timer->task,
563 timer,
564 ISC_TIMEREVENT_FIRSTEVENT,
565 ISC_TIMEREVENT_LASTEVENT,
566 NULL);
567 timer->type = type;
568 timer->expires = *expires;
569 timer->interval = *interval;
570 if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
571 result = isc_time_add(&now, interval, &timer->idle);
572 } else {
573 isc_time_settoepoch(&timer->idle);
574 result = ISC_R_SUCCESS;
575 }
576
577 if (result == ISC_R_SUCCESS) {
578 if (type == isc_timertype_inactive) {
579 deschedule(timer);
580 result = ISC_R_SUCCESS;
581 } else
582 result = schedule(timer, &now, ISC_TRUE);
583 }
584
585 UNLOCK(&timer->lock);
586 UNLOCK(&manager->lock);
587
588 return (result);
589 }
590
591 ISC_TIMERFUNC_SCOPE isc_timertype_t
isc__timer_gettype(isc_timer_t * timer0)592 isc__timer_gettype(isc_timer_t *timer0) {
593 isc__timer_t *timer = (isc__timer_t *)timer0;
594 isc_timertype_t t;
595
596 REQUIRE(VALID_TIMER(timer));
597
598 LOCK(&timer->lock);
599 t = timer->type;
600 UNLOCK(&timer->lock);
601
602 return (t);
603 }
604
605 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_touch(isc_timer_t * timer0)606 isc__timer_touch(isc_timer_t *timer0) {
607 isc__timer_t *timer = (isc__timer_t *)timer0;
608 isc_result_t result;
609 isc_time_t now;
610
611 /*
612 * Set the last-touched time of 'timer' to the current time.
613 */
614
615 REQUIRE(VALID_TIMER(timer));
616
617 LOCK(&timer->lock);
618
619 /*
620 * We'd like to
621 *
622 * REQUIRE(timer->type == isc_timertype_once);
623 *
624 * but we cannot without locking the manager lock too, which we
625 * don't want to do.
626 */
627
628 TIME_NOW(&now);
629 result = isc_time_add(&now, &timer->interval, &timer->idle);
630
631 UNLOCK(&timer->lock);
632
633 return (result);
634 }
635
636 ISC_TIMERFUNC_SCOPE void
isc__timer_attach(isc_timer_t * timer0,isc_timer_t ** timerp)637 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
638 isc__timer_t *timer = (isc__timer_t *)timer0;
639
640 /*
641 * Attach *timerp to timer.
642 */
643
644 REQUIRE(VALID_TIMER(timer));
645 REQUIRE(timerp != NULL && *timerp == NULL);
646
647 LOCK(&timer->lock);
648 timer->references++;
649 UNLOCK(&timer->lock);
650
651 *timerp = (isc_timer_t *)timer;
652 }
653
654 ISC_TIMERFUNC_SCOPE void
isc__timer_detach(isc_timer_t ** timerp)655 isc__timer_detach(isc_timer_t **timerp) {
656 isc__timer_t *timer;
657 isc_boolean_t free_timer = ISC_FALSE;
658
659 /*
660 * Detach *timerp from its timer.
661 */
662
663 REQUIRE(timerp != NULL);
664 timer = (isc__timer_t *)*timerp;
665 REQUIRE(VALID_TIMER(timer));
666
667 LOCK(&timer->lock);
668 REQUIRE(timer->references > 0);
669 timer->references--;
670 if (timer->references == 0)
671 free_timer = ISC_TRUE;
672 UNLOCK(&timer->lock);
673
674 if (free_timer)
675 destroy(timer);
676
677 *timerp = NULL;
678 }
679
680 static void
dispatch(isc__timermgr_t * manager,isc_time_t * now)681 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
682 isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
683 isc_timerevent_t *event;
684 isc_eventtype_t type = 0;
685 isc__timer_t *timer;
686 isc_result_t result;
687 isc_boolean_t idle;
688
689 /*!
690 * The caller must be holding the manager lock.
691 */
692
693 while (manager->nscheduled > 0 && !done) {
694 timer = isc_heap_element(manager->heap, 1);
695 INSIST(timer->type != isc_timertype_inactive);
696 if (isc_time_compare(now, &timer->due) >= 0) {
697 if (timer->type == isc_timertype_ticker) {
698 type = ISC_TIMEREVENT_TICK;
699 post_event = ISC_TRUE;
700 need_schedule = ISC_TRUE;
701 } else if (timer->type == isc_timertype_limited) {
702 int cmp;
703 cmp = isc_time_compare(now, &timer->expires);
704 if (cmp >= 0) {
705 type = ISC_TIMEREVENT_LIFE;
706 post_event = ISC_TRUE;
707 need_schedule = ISC_FALSE;
708 } else {
709 type = ISC_TIMEREVENT_TICK;
710 post_event = ISC_TRUE;
711 need_schedule = ISC_TRUE;
712 }
713 } else if (!isc_time_isepoch(&timer->expires) &&
714 isc_time_compare(now,
715 &timer->expires) >= 0) {
716 type = ISC_TIMEREVENT_LIFE;
717 post_event = ISC_TRUE;
718 need_schedule = ISC_FALSE;
719 } else {
720 idle = ISC_FALSE;
721
722 LOCK(&timer->lock);
723 if (!isc_time_isepoch(&timer->idle) &&
724 isc_time_compare(now,
725 &timer->idle) >= 0) {
726 idle = ISC_TRUE;
727 }
728 UNLOCK(&timer->lock);
729 if (idle) {
730 type = ISC_TIMEREVENT_IDLE;
731 post_event = ISC_TRUE;
732 need_schedule = ISC_FALSE;
733 } else {
734 /*
735 * Idle timer has been touched;
736 * reschedule.
737 */
738 XTRACEID(isc_msgcat_get(isc_msgcat,
739 ISC_MSGSET_TIMER,
740 ISC_MSG_IDLERESCHED,
741 "idle reschedule"),
742 timer);
743 post_event = ISC_FALSE;
744 need_schedule = ISC_TRUE;
745 }
746 }
747
748 if (post_event) {
749 XTRACEID(isc_msgcat_get(isc_msgcat,
750 ISC_MSGSET_TIMER,
751 ISC_MSG_POSTING,
752 "posting"), timer);
753 /*
754 * XXX We could preallocate this event.
755 */
756 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
757 timer,
758 type,
759 timer->action,
760 timer->arg,
761 sizeof(*event));
762
763 if (event != NULL) {
764 event->due = timer->due;
765 isc_task_send(timer->task,
766 ISC_EVENT_PTR(&event));
767 } else
768 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
769 isc_msgcat_get(isc_msgcat,
770 ISC_MSGSET_TIMER,
771 ISC_MSG_EVENTNOTALLOC,
772 "couldn't "
773 "allocate event"));
774 }
775
776 timer->index = 0;
777 isc_heap_delete(manager->heap, 1);
778 manager->nscheduled--;
779
780 if (need_schedule) {
781 result = schedule(timer, now, ISC_FALSE);
782 if (result != ISC_R_SUCCESS)
783 UNEXPECTED_ERROR(__FILE__, __LINE__,
784 "%s: %u",
785 isc_msgcat_get(isc_msgcat,
786 ISC_MSGSET_TIMER,
787 ISC_MSG_SCHEDFAIL,
788 "couldn't schedule "
789 "timer"),
790 result);
791 }
792 } else {
793 manager->due = timer->due;
794 done = ISC_TRUE;
795 }
796 }
797 }
798
799 #ifdef USE_TIMER_THREAD
800 static isc_threadresult_t
801 #ifdef _WIN32 /* XXXDCL */
802 WINAPI
803 #endif
run(void * uap)804 run(void *uap) {
805 isc__timermgr_t *manager = uap;
806 isc_time_t now;
807 isc_result_t result;
808
809 LOCK(&manager->lock);
810 while (!manager->done) {
811 TIME_NOW(&now);
812
813 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
814 ISC_MSG_RUNNING,
815 "running"), now);
816
817 dispatch(manager, &now);
818
819 if (manager->nscheduled > 0) {
820 XTRACETIME2(isc_msgcat_get(isc_msgcat,
821 ISC_MSGSET_GENERAL,
822 ISC_MSG_WAITUNTIL,
823 "waituntil"),
824 manager->due, now);
825 result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
826 INSIST(result == ISC_R_SUCCESS ||
827 result == ISC_R_TIMEDOUT);
828 } else {
829 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
830 ISC_MSG_WAIT, "wait"), now);
831 WAIT(&manager->wakeup, &manager->lock);
832 }
833 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
834 ISC_MSG_WAKEUP, "wakeup"));
835 }
836 UNLOCK(&manager->lock);
837
838 #ifdef OPENSSL_LEAKS
839 ERR_remove_state(0);
840 #endif
841
842 return ((isc_threadresult_t)0);
843 }
844 #endif /* USE_TIMER_THREAD */
845
846 static isc_boolean_t
sooner(void * v1,void * v2)847 sooner(void *v1, void *v2) {
848 isc__timer_t *t1, *t2;
849
850 t1 = v1;
851 t2 = v2;
852 REQUIRE(VALID_TIMER(t1));
853 REQUIRE(VALID_TIMER(t2));
854
855 if (isc_time_compare(&t1->due, &t2->due) < 0)
856 return (ISC_TRUE);
857 return (ISC_FALSE);
858 }
859
860 static void
set_index(void * what,unsigned int index)861 set_index(void *what, unsigned int index) {
862 isc__timer_t *timer;
863
864 timer = what;
865 REQUIRE(VALID_TIMER(timer));
866
867 timer->index = index;
868 }
869
870 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timermgr_create(isc_mem_t * mctx,isc_timermgr_t ** managerp)871 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
872 isc__timermgr_t *manager;
873 isc_result_t result;
874
875 /*
876 * Create a timer manager.
877 */
878
879 REQUIRE(managerp != NULL && *managerp == NULL);
880
881 #ifdef USE_SHARED_MANAGER
882 if (timermgr != NULL) {
883 timermgr->refs++;
884 *managerp = (isc_timermgr_t *)timermgr;
885 return (ISC_R_SUCCESS);
886 }
887 #endif /* USE_SHARED_MANAGER */
888
889 manager = isc_mem_get(mctx, sizeof(*manager));
890 if (manager == NULL)
891 return (ISC_R_NOMEMORY);
892
893 manager->common.impmagic = TIMER_MANAGER_MAGIC;
894 manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
895 manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
896 manager->mctx = NULL;
897 manager->done = ISC_FALSE;
898 INIT_LIST(manager->timers);
899 manager->nscheduled = 0;
900 isc_time_settoepoch(&manager->due);
901 manager->heap = NULL;
902 result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
903 if (result != ISC_R_SUCCESS) {
904 INSIST(result == ISC_R_NOMEMORY);
905 isc_mem_put(mctx, manager, sizeof(*manager));
906 return (ISC_R_NOMEMORY);
907 }
908 result = isc_mutex_init(&manager->lock);
909 if (result != ISC_R_SUCCESS) {
910 isc_heap_destroy(&manager->heap);
911 isc_mem_put(mctx, manager, sizeof(*manager));
912 return (result);
913 }
914 isc_mem_attach(mctx, &manager->mctx);
915 #ifdef USE_TIMER_THREAD
916 if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
917 isc_mem_detach(&manager->mctx);
918 DESTROYLOCK(&manager->lock);
919 isc_heap_destroy(&manager->heap);
920 isc_mem_put(mctx, manager, sizeof(*manager));
921 UNEXPECTED_ERROR(__FILE__, __LINE__,
922 "isc_condition_init() %s",
923 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
924 ISC_MSG_FAILED, "failed"));
925 return (ISC_R_UNEXPECTED);
926 }
927 if (isc_thread_create(run, manager, &manager->thread) !=
928 ISC_R_SUCCESS) {
929 isc_mem_detach(&manager->mctx);
930 (void)isc_condition_destroy(&manager->wakeup);
931 DESTROYLOCK(&manager->lock);
932 isc_heap_destroy(&manager->heap);
933 isc_mem_put(mctx, manager, sizeof(*manager));
934 UNEXPECTED_ERROR(__FILE__, __LINE__,
935 "isc_thread_create() %s",
936 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
937 ISC_MSG_FAILED, "failed"));
938 return (ISC_R_UNEXPECTED);
939 }
940 #endif
941 #ifdef USE_SHARED_MANAGER
942 manager->refs = 1;
943 timermgr = manager;
944 #endif /* USE_SHARED_MANAGER */
945
946 *managerp = (isc_timermgr_t *)manager;
947
948 return (ISC_R_SUCCESS);
949 }
950
951 ISC_TIMERFUNC_SCOPE void
isc__timermgr_poke(isc_timermgr_t * manager0)952 isc__timermgr_poke(isc_timermgr_t *manager0) {
953 #ifdef USE_TIMER_THREAD
954 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
955
956 REQUIRE(VALID_MANAGER(manager));
957
958 SIGNAL(&manager->wakeup);
959 #else
960 UNUSED(manager0);
961 #endif
962 }
963
964 ISC_TIMERFUNC_SCOPE void
isc__timermgr_destroy(isc_timermgr_t ** managerp)965 isc__timermgr_destroy(isc_timermgr_t **managerp) {
966 isc__timermgr_t *manager;
967 isc_mem_t *mctx;
968
969 /*
970 * Destroy a timer manager.
971 */
972
973 REQUIRE(managerp != NULL);
974 manager = (isc__timermgr_t *)*managerp;
975 REQUIRE(VALID_MANAGER(manager));
976
977 LOCK(&manager->lock);
978
979 #ifdef USE_SHARED_MANAGER
980 manager->refs--;
981 if (manager->refs > 0) {
982 UNLOCK(&manager->lock);
983 *managerp = NULL;
984 return;
985 }
986 timermgr = NULL;
987 #endif /* USE_SHARED_MANAGER */
988
989 #ifndef USE_TIMER_THREAD
990 isc__timermgr_dispatch((isc_timermgr_t *)manager);
991 #endif
992
993 REQUIRE(EMPTY(manager->timers));
994 manager->done = ISC_TRUE;
995
996 #ifdef USE_TIMER_THREAD
997 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
998 ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
999 SIGNAL(&manager->wakeup);
1000 #endif /* USE_TIMER_THREAD */
1001
1002 UNLOCK(&manager->lock);
1003
1004 #ifdef USE_TIMER_THREAD
1005 /*
1006 * Wait for thread to exit.
1007 */
1008 if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
1009 UNEXPECTED_ERROR(__FILE__, __LINE__,
1010 "isc_thread_join() %s",
1011 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
1012 ISC_MSG_FAILED, "failed"));
1013 #endif /* USE_TIMER_THREAD */
1014
1015 /*
1016 * Clean up.
1017 */
1018 #ifdef USE_TIMER_THREAD
1019 (void)isc_condition_destroy(&manager->wakeup);
1020 #endif /* USE_TIMER_THREAD */
1021 DESTROYLOCK(&manager->lock);
1022 isc_heap_destroy(&manager->heap);
1023 manager->common.impmagic = 0;
1024 manager->common.magic = 0;
1025 mctx = manager->mctx;
1026 isc_mem_put(mctx, manager, sizeof(*manager));
1027 isc_mem_detach(&mctx);
1028
1029 *managerp = NULL;
1030
1031 #ifdef USE_SHARED_MANAGER
1032 timermgr = NULL;
1033 #endif
1034 }
1035
1036 #ifndef USE_TIMER_THREAD
1037 isc_result_t
isc__timermgr_nextevent(isc_timermgr_t * manager0,isc_time_t * when)1038 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
1039 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1040
1041 #ifdef USE_SHARED_MANAGER
1042 if (manager == NULL)
1043 manager = timermgr;
1044 #endif
1045 if (manager == NULL || manager->nscheduled == 0)
1046 return (ISC_R_NOTFOUND);
1047 *when = manager->due;
1048 return (ISC_R_SUCCESS);
1049 }
1050
1051 void
isc__timermgr_dispatch(isc_timermgr_t * manager0)1052 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
1053 isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
1054 isc_time_t now;
1055
1056 #ifdef USE_SHARED_MANAGER
1057 if (manager == NULL)
1058 manager = timermgr;
1059 #endif
1060 if (manager == NULL)
1061 return;
1062 TIME_NOW(&now);
1063 dispatch(manager, &now);
1064 }
1065 #endif /* USE_TIMER_THREAD */
1066
1067 #ifdef USE_TIMERIMPREGISTER
1068 isc_result_t
isc__timer_register()1069 isc__timer_register() {
1070 return (isc_timer_register(isc__timermgr_create));
1071 }
1072 #endif
1073