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