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