xref: /openbsd-src/usr.bin/dig/lib/isc/timer.c (revision 5185a7002a54c85c252cec8ac1712898b2c4d364)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /* $Id: timer.c,v 1.1 2020/02/07 09:58:54 florian Exp $ */
18 
19 /*! \file */
20 
21 
22 #include <stdlib.h>
23 #include <isc/app.h>
24 #include <isc/heap.h>
25 #include <isc/log.h>
26 #include <isc/magic.h>
27 #include <isc/msgs.h>
28 #include <isc/task.h>
29 #include <isc/time.h>
30 #include <isc/timer.h>
31 #include <isc/util.h>
32 
33 #include "timer_p.h"
34 
35 #ifdef ISC_TIMER_TRACE
36 #define XTRACE(s)			fprintf(stderr, "%s\n", (s))
37 #define XTRACEID(s, t)			fprintf(stderr, "%s %p\n", (s), (t))
38 #define XTRACETIME(s, d)		fprintf(stderr, "%s %u.%09u\n", (s), \
39 					       (d).seconds, (d).nanoseconds)
40 #define XTRACETIME2(s, d, n)		fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
41 					       (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
42 #define XTRACETIMER(s, t, d)		fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
43 					       (d).seconds, (d).nanoseconds)
44 #else
45 #define XTRACE(s)
46 #define XTRACEID(s, t)
47 #define XTRACETIME(s, d)
48 #define XTRACETIME2(s, d, n)
49 #define XTRACETIMER(s, t, d)
50 #endif /* ISC_TIMER_TRACE */
51 
52 #define TIMER_MAGIC			ISC_MAGIC('T', 'I', 'M', 'R')
53 #define VALID_TIMER(t)			ISC_MAGIC_VALID(t, TIMER_MAGIC)
54 
55 typedef struct isc__timer isc__timer_t;
56 typedef struct isc__timermgr isc__timermgr_t;
57 
58 struct isc__timer {
59 	/*! Not locked. */
60 	isc_timer_t			common;
61 	isc__timermgr_t *		manager;
62 	/*! Locked by timer lock. */
63 	unsigned int			references;
64 	isc_time_t			idle;
65 	/*! Locked by manager lock. */
66 	isc_timertype_t			type;
67 	isc_time_t			expires;
68 	interval_t			interval;
69 	isc_task_t *			task;
70 	isc_taskaction_t		action;
71 	void *				arg;
72 	unsigned int			index;
73 	isc_time_t			due;
74 	LINK(isc__timer_t)		link;
75 };
76 
77 #define TIMER_MANAGER_MAGIC		ISC_MAGIC('T', 'I', 'M', 'M')
78 #define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
79 
80 struct isc__timermgr {
81 	/* Not locked. */
82 	isc_timermgr_t			common;
83 	/* Locked by manager lock. */
84 	isc_boolean_t			done;
85 	LIST(isc__timer_t)		timers;
86 	unsigned int			nscheduled;
87 	isc_time_t			due;
88 	unsigned int			refs;
89 	isc_heap_t *			heap;
90 };
91 
92 /*%
93  * The following are intended for internal use (indicated by "isc__"
94  * prefix) but are not declared as static, allowing direct access from
95  * unit tests etc.
96  */
97 
98 isc_result_t
99 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
100 		  const isc_time_t *expires, const interval_t *interval,
101 		  isc_task_t *task, isc_taskaction_t action, void *arg,
102 		  isc_timer_t **timerp);
103 isc_result_t
104 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
105 		 const isc_time_t *expires, const interval_t *interval,
106 		 isc_boolean_t purge);
107 isc_timertype_t
108 isc_timer_gettype(isc_timer_t *timer);
109 isc_result_t
110 isc__timer_touch(isc_timer_t *timer);
111 void
112 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
113 void
114 isc__timer_detach(isc_timer_t **timerp);
115 isc_result_t
116 isc__timermgr_create(isc_timermgr_t **managerp);
117 void
118 isc_timermgr_poke(isc_timermgr_t *manager0);
119 void
120 isc__timermgr_destroy(isc_timermgr_t **managerp);
121 
122 static struct isc__timermethods {
123 	isc_timermethods_t methods;
124 
125 	/*%
126 	 * The following are defined just for avoiding unused static functions.
127 	 */
128 	void *gettype;
129 } timermethods = {
130 	{
131 		isc__timer_attach,
132 		isc__timer_detach,
133 		isc__timer_reset,
134 		isc__timer_touch
135 	},
136 	(void *)isc_timer_gettype
137 };
138 
139 static struct isc__timermgrmethods {
140 	isc_timermgrmethods_t methods;
141 	void *poke;		/* see above */
142 } timermgrmethods = {
143 	{
144 		isc__timermgr_destroy,
145 		isc__timer_create
146 	},
147 	(void *)isc_timermgr_poke
148 };
149 
150 /*!
151  * If the manager is supposed to be shared, there can be only one.
152  */
153 static isc__timermgr_t *timermgr = NULL;
154 
155 static inline isc_result_t
156 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
157 	isc_result_t result;
158 	isc__timermgr_t *manager;
159 	isc_time_t due;
160 	int cmp;
161 
162 	/*!
163 	 * Note: the caller must ensure locking.
164 	 */
165 
166 	REQUIRE(timer->type != isc_timertype_inactive);
167 
168 	UNUSED(signal_ok);
169 
170 	manager = timer->manager;
171 
172 	/*
173 	 * Compute the new due time.
174 	 */
175 	if (timer->type != isc_timertype_once) {
176 		result = isc_time_add(now, &timer->interval, &due);
177 		if (result != ISC_R_SUCCESS)
178 			return (result);
179 		if (timer->type == isc_timertype_limited &&
180 		    isc_time_compare(&timer->expires, &due) < 0)
181 			due = timer->expires;
182 	} else {
183 		if (isc_time_isepoch(&timer->idle))
184 			due = timer->expires;
185 		else if (isc_time_isepoch(&timer->expires))
186 			due = timer->idle;
187 		else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
188 			due = timer->idle;
189 		else
190 			due = timer->expires;
191 	}
192 
193 	/*
194 	 * Schedule the timer.
195 	 */
196 
197 	if (timer->index > 0) {
198 		/*
199 		 * Already scheduled.
200 		 */
201 		cmp = isc_time_compare(&due, &timer->due);
202 		timer->due = due;
203 		switch (cmp) {
204 		case -1:
205 			isc_heap_increased(manager->heap, timer->index);
206 			break;
207 		case 1:
208 			isc_heap_decreased(manager->heap, timer->index);
209 			break;
210 		case 0:
211 			/* Nothing to do. */
212 			break;
213 		}
214 	} else {
215 		timer->due = due;
216 		result = isc_heap_insert(manager->heap, timer);
217 		if (result != ISC_R_SUCCESS) {
218 			INSIST(result == ISC_R_NOMEMORY);
219 			return (ISC_R_NOMEMORY);
220 		}
221 		manager->nscheduled++;
222 	}
223 
224 	XTRACETIMER("schedule", timer, due);
225 
226 	/*
227 	 * If this timer is at the head of the queue, we need to ensure
228 	 * that we won't miss it if it has a more recent due time than
229 	 * the current "next" timer.  We do this either by waking up the
230 	 * run thread, or explicitly setting the value in the manager.
231 	 */
232 	if (timer->index == 1 &&
233 	    isc_time_compare(&timer->due, &manager->due) < 0)
234 		manager->due = timer->due;
235 
236 	return (ISC_R_SUCCESS);
237 }
238 
239 static inline void
240 deschedule(isc__timer_t *timer) {
241 	isc__timermgr_t *manager;
242 
243 	/*
244 	 * The caller must ensure locking.
245 	 */
246 
247 	manager = timer->manager;
248 	if (timer->index > 0) {
249 		isc_heap_delete(manager->heap, timer->index);
250 		timer->index = 0;
251 		INSIST(manager->nscheduled > 0);
252 		manager->nscheduled--;
253 	}
254 }
255 
256 static void
257 destroy(isc__timer_t *timer) {
258 	isc__timermgr_t *manager = timer->manager;
259 
260 	/*
261 	 * The caller must ensure it is safe to destroy the timer.
262 	 */
263 
264 	(void)isc_task_purgerange(timer->task,
265 				  timer,
266 				  ISC_TIMEREVENT_FIRSTEVENT,
267 				  ISC_TIMEREVENT_LASTEVENT,
268 				  NULL);
269 	deschedule(timer);
270 	UNLINK(manager->timers, timer, link);
271 
272 	isc_task_detach(&timer->task);
273 	timer->common.impmagic = 0;
274 	timer->common.magic = 0;
275 	free(timer);
276 }
277 
278 isc_result_t
279 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
280 		  const isc_time_t *expires, const interval_t *interval,
281 		  isc_task_t *task, isc_taskaction_t action, void *arg,
282 		  isc_timer_t **timerp)
283 {
284 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
285 	isc__timer_t *timer;
286 	isc_result_t result;
287 	isc_time_t now;
288 
289 	/*
290 	 * Create a new 'type' timer managed by 'manager'.  The timers
291 	 * parameters are specified by 'expires' and 'interval'.  Events
292 	 * will be posted to 'task' and when dispatched 'action' will be
293 	 * called with 'arg' as the arg value.  The new timer is returned
294 	 * in 'timerp'.
295 	 */
296 
297 	REQUIRE(VALID_MANAGER(manager));
298 	REQUIRE(task != NULL);
299 	REQUIRE(action != NULL);
300 	if (expires == NULL)
301 		expires = isc_time_epoch;
302 	if (interval == NULL)
303 		interval = interval_zero;
304 	REQUIRE(type == isc_timertype_inactive ||
305 		!(isc_time_isepoch(expires) && interval_iszero(interval)));
306 	REQUIRE(timerp != NULL && *timerp == NULL);
307 	REQUIRE(type != isc_timertype_limited ||
308 		!(isc_time_isepoch(expires) || interval_iszero(interval)));
309 
310 	/*
311 	 * Get current time.
312 	 */
313 	if (type != isc_timertype_inactive) {
314 		TIME_NOW(&now);
315 	} else {
316 		/*
317 		 * We don't have to do this, but it keeps the compiler from
318 		 * complaining about "now" possibly being used without being
319 		 * set, even though it will never actually happen.
320 		 */
321 		isc_time_settoepoch(&now);
322 	}
323 
324 
325 	timer = malloc(sizeof(*timer));
326 	if (timer == NULL)
327 		return (ISC_R_NOMEMORY);
328 
329 	timer->manager = manager;
330 	timer->references = 1;
331 
332 	if (type == isc_timertype_once && !interval_iszero(interval)) {
333 		result = isc_time_add(&now, interval, &timer->idle);
334 		if (result != ISC_R_SUCCESS) {
335 			free(timer);
336 			return (result);
337 		}
338 	} else
339 		isc_time_settoepoch(&timer->idle);
340 
341 	timer->type = type;
342 	timer->expires = *expires;
343 	timer->interval = *interval;
344 	timer->task = NULL;
345 	isc_task_attach(task, &timer->task);
346 	timer->action = action;
347 	/*
348 	 * Removing the const attribute from "arg" is the best of two
349 	 * evils here.  If the timer->arg member is made const, then
350 	 * it affects a great many recipients of the timer event
351 	 * which did not pass in an "arg" that was truly const.
352 	 * Changing isc_timer_create() to not have "arg" prototyped as const,
353 	 * though, can cause compilers warnings for calls that *do*
354 	 * have a truly const arg.  The caller will have to carefully
355 	 * keep track of whether arg started as a true const.
356 	 */
357 	DE_CONST(arg, timer->arg);
358 	timer->index = 0;
359 	ISC_LINK_INIT(timer, link);
360 	timer->common.impmagic = TIMER_MAGIC;
361 	timer->common.magic = ISCAPI_TIMER_MAGIC;
362 	timer->common.methods = (isc_timermethods_t *)&timermethods;
363 
364 	if (type != isc_timertype_inactive)
365 		result = schedule(timer, &now, ISC_TRUE);
366 	else
367 		result = ISC_R_SUCCESS;
368 	if (result == ISC_R_SUCCESS)
369 		APPEND(manager->timers, timer, link);
370 
371 	if (result != ISC_R_SUCCESS) {
372 		timer->common.impmagic = 0;
373 		timer->common.magic = 0;
374 		isc_task_detach(&timer->task);
375 		free(timer);
376 		return (result);
377 	}
378 
379 	*timerp = (isc_timer_t *)timer;
380 
381 	return (ISC_R_SUCCESS);
382 }
383 
384 isc_result_t
385 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
386 		 const isc_time_t *expires, const interval_t *interval,
387 		 isc_boolean_t purge)
388 {
389 	isc__timer_t *timer = (isc__timer_t *)timer0;
390 	isc_time_t now;
391 	isc__timermgr_t *manager;
392 	isc_result_t result;
393 
394 	/*
395 	 * Change the timer's type, expires, and interval values to the given
396 	 * values.  If 'purge' is ISC_TRUE, any pending events from this timer
397 	 * are purged from its task's event queue.
398 	 */
399 
400 	REQUIRE(VALID_TIMER(timer));
401 	manager = timer->manager;
402 	REQUIRE(VALID_MANAGER(manager));
403 
404 	if (expires == NULL)
405 		expires = isc_time_epoch;
406 	if (interval == NULL)
407 		interval = interval_zero;
408 	REQUIRE(type == isc_timertype_inactive ||
409 		!(isc_time_isepoch(expires) && interval_iszero(interval)));
410 	REQUIRE(type != isc_timertype_limited ||
411 		!(isc_time_isepoch(expires) || interval_iszero(interval)));
412 
413 	/*
414 	 * Get current time.
415 	 */
416 	if (type != isc_timertype_inactive) {
417 		TIME_NOW(&now);
418 	} else {
419 		/*
420 		 * We don't have to do this, but it keeps the compiler from
421 		 * complaining about "now" possibly being used without being
422 		 * set, even though it will never actually happen.
423 		 */
424 		isc_time_settoepoch(&now);
425 	}
426 
427 	if (purge)
428 		(void)isc_task_purgerange(timer->task,
429 					  timer,
430 					  ISC_TIMEREVENT_FIRSTEVENT,
431 					  ISC_TIMEREVENT_LASTEVENT,
432 					  NULL);
433 	timer->type = type;
434 	timer->expires = *expires;
435 	timer->interval = *interval;
436 	if (type == isc_timertype_once && !interval_iszero(interval)) {
437 		result = isc_time_add(&now, interval, &timer->idle);
438 	} else {
439 		isc_time_settoepoch(&timer->idle);
440 		result = ISC_R_SUCCESS;
441 	}
442 
443 	if (result == ISC_R_SUCCESS) {
444 		if (type == isc_timertype_inactive) {
445 			deschedule(timer);
446 			result = ISC_R_SUCCESS;
447 		} else
448 			result = schedule(timer, &now, ISC_TRUE);
449 	}
450 
451 	return (result);
452 }
453 
454 isc_timertype_t
455 isc_timer_gettype(isc_timer_t *timer0) {
456 	isc__timer_t *timer = (isc__timer_t *)timer0;
457 	isc_timertype_t t;
458 
459 	REQUIRE(VALID_TIMER(timer));
460 
461 	t = timer->type;
462 
463 	return (t);
464 }
465 
466 isc_result_t
467 isc__timer_touch(isc_timer_t *timer0) {
468 	isc__timer_t *timer = (isc__timer_t *)timer0;
469 	isc_result_t result;
470 	isc_time_t now;
471 
472 	/*
473 	 * Set the last-touched time of 'timer' to the current time.
474 	 */
475 
476 	REQUIRE(VALID_TIMER(timer));
477 
478 	TIME_NOW(&now);
479 	result = isc_time_add(&now, &timer->interval, &timer->idle);
480 
481 	return (result);
482 }
483 
484 void
485 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
486 	isc__timer_t *timer = (isc__timer_t *)timer0;
487 
488 	/*
489 	 * Attach *timerp to timer.
490 	 */
491 
492 	REQUIRE(VALID_TIMER(timer));
493 	REQUIRE(timerp != NULL && *timerp == NULL);
494 
495 	timer->references++;
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 	isc_boolean_t free_timer = ISC_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 	REQUIRE(timer->references > 0);
514 	timer->references--;
515 	if (timer->references == 0)
516 		free_timer = ISC_TRUE;
517 
518 	if (free_timer)
519 		destroy(timer);
520 
521 	*timerp = NULL;
522 }
523 
524 static void
525 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
526 	isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
527 	isc_timerevent_t *event;
528 	isc_eventtype_t type = 0;
529 	isc__timer_t *timer;
530 	isc_result_t result;
531 	isc_boolean_t idle;
532 
533 	/*!
534 	 * The caller must be holding the manager lock.
535 	 */
536 
537 	while (manager->nscheduled > 0 && !done) {
538 		timer = isc_heap_element(manager->heap, 1);
539 		INSIST(timer != NULL && timer->type != isc_timertype_inactive);
540 		if (isc_time_compare(now, &timer->due) >= 0) {
541 			if (timer->type == isc_timertype_ticker) {
542 				type = ISC_TIMEREVENT_TICK;
543 				post_event = ISC_TRUE;
544 				need_schedule = ISC_TRUE;
545 			} else if (timer->type == isc_timertype_limited) {
546 				int cmp;
547 				cmp = isc_time_compare(now, &timer->expires);
548 				if (cmp >= 0) {
549 					type = ISC_TIMEREVENT_LIFE;
550 					post_event = ISC_TRUE;
551 					need_schedule = ISC_FALSE;
552 				} else {
553 					type = ISC_TIMEREVENT_TICK;
554 					post_event = ISC_TRUE;
555 					need_schedule = ISC_TRUE;
556 				}
557 			} else if (!isc_time_isepoch(&timer->expires) &&
558 				   isc_time_compare(now,
559 						    &timer->expires) >= 0) {
560 				type = ISC_TIMEREVENT_LIFE;
561 				post_event = ISC_TRUE;
562 				need_schedule = ISC_FALSE;
563 			} else {
564 				idle = ISC_FALSE;
565 
566 				if (!isc_time_isepoch(&timer->idle) &&
567 				    isc_time_compare(now,
568 						     &timer->idle) >= 0) {
569 					idle = ISC_TRUE;
570 				}
571 				if (idle) {
572 					type = ISC_TIMEREVENT_IDLE;
573 					post_event = ISC_TRUE;
574 					need_schedule = ISC_FALSE;
575 				} else {
576 					/*
577 					 * Idle timer has been touched;
578 					 * reschedule.
579 					 */
580 					XTRACEID("idle reschedule", timer);
581 					post_event = ISC_FALSE;
582 					need_schedule = ISC_TRUE;
583 				}
584 			}
585 
586 			if (post_event) {
587 				XTRACEID("posting", timer);
588 				/*
589 				 * XXX We could preallocate this event.
590 				 */
591 				event = (isc_timerevent_t *)isc_event_allocate(
592 							   timer,
593 							   type,
594 							   timer->action,
595 							   timer->arg,
596 							   sizeof(*event));
597 
598 				if (event != NULL) {
599 					event->due = timer->due;
600 					isc_task_send(timer->task,
601 						      ISC_EVENT_PTR(&event));
602 				} else
603 					UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
604 						 "couldn't allocate event");
605 			}
606 
607 			timer->index = 0;
608 			isc_heap_delete(manager->heap, 1);
609 			manager->nscheduled--;
610 
611 			if (need_schedule) {
612 				result = schedule(timer, now, ISC_FALSE);
613 				if (result != ISC_R_SUCCESS)
614 					UNEXPECTED_ERROR(__FILE__, __LINE__,
615 						"%s: %u",
616 						"couldn't schedule timer",
617 						result);
618 			}
619 		} else {
620 			manager->due = timer->due;
621 			done = ISC_TRUE;
622 		}
623 	}
624 }
625 
626 static isc_boolean_t
627 sooner(void *v1, void *v2) {
628 	isc__timer_t *t1, *t2;
629 
630 	t1 = v1;
631 	t2 = v2;
632 	REQUIRE(VALID_TIMER(t1));
633 	REQUIRE(VALID_TIMER(t2));
634 
635 	if (isc_time_compare(&t1->due, &t2->due) < 0)
636 		return (ISC_TRUE);
637 	return (ISC_FALSE);
638 }
639 
640 static void
641 set_index(void *what, unsigned int index) {
642 	isc__timer_t *timer;
643 
644 	timer = what;
645 	REQUIRE(VALID_TIMER(timer));
646 
647 	timer->index = index;
648 }
649 
650 isc_result_t
651 isc__timermgr_create(isc_timermgr_t **managerp) {
652 	isc__timermgr_t *manager;
653 	isc_result_t result;
654 
655 	/*
656 	 * Create a timer manager.
657 	 */
658 
659 	REQUIRE(managerp != NULL && *managerp == NULL);
660 
661 	if (timermgr != NULL) {
662 		timermgr->refs++;
663 		*managerp = (isc_timermgr_t *)timermgr;
664 		return (ISC_R_SUCCESS);
665 	}
666 
667 	manager = malloc(sizeof(*manager));
668 	if (manager == NULL)
669 		return (ISC_R_NOMEMORY);
670 
671 	manager->common.impmagic = TIMER_MANAGER_MAGIC;
672 	manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
673 	manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
674 	manager->done = ISC_FALSE;
675 	INIT_LIST(manager->timers);
676 	manager->nscheduled = 0;
677 	isc_time_settoepoch(&manager->due);
678 	manager->heap = NULL;
679 	result = isc_heap_create(sooner, set_index, 0, &manager->heap);
680 	if (result != ISC_R_SUCCESS) {
681 		INSIST(result == ISC_R_NOMEMORY);
682 		free(manager);
683 		return (ISC_R_NOMEMORY);
684 	}
685 	manager->refs = 1;
686 	timermgr = manager;
687 
688 	*managerp = (isc_timermgr_t *)manager;
689 
690 	return (ISC_R_SUCCESS);
691 }
692 
693 void
694 isc_timermgr_poke(isc_timermgr_t *manager0) {
695 	UNUSED(manager0);
696 }
697 
698 void
699 isc__timermgr_destroy(isc_timermgr_t **managerp) {
700 	isc__timermgr_t *manager;
701 
702 	/*
703 	 * Destroy a timer manager.
704 	 */
705 
706 	REQUIRE(managerp != NULL);
707 	manager = (isc__timermgr_t *)*managerp;
708 	REQUIRE(VALID_MANAGER(manager));
709 
710 	manager->refs--;
711 	if (manager->refs > 0) {
712 		*managerp = NULL;
713 		return;
714 	}
715 	timermgr = NULL;
716 
717 	isc__timermgr_dispatch((isc_timermgr_t *)manager);
718 
719 	REQUIRE(EMPTY(manager->timers));
720 	manager->done = ISC_TRUE;
721 
722 	/*
723 	 * Clean up.
724 	 */
725 	isc_heap_destroy(&manager->heap);
726 	manager->common.impmagic = 0;
727 	manager->common.magic = 0;
728 	free(manager);
729 
730 	*managerp = NULL;
731 
732 	timermgr = NULL;
733 }
734 
735 isc_result_t
736 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
737 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
738 
739 	if (manager == NULL)
740 		manager = timermgr;
741 	if (manager == NULL || manager->nscheduled == 0)
742 		return (ISC_R_NOTFOUND);
743 	*when = manager->due;
744 	return (ISC_R_SUCCESS);
745 }
746 
747 void
748 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
749 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
750 	isc_time_t now;
751 
752 	if (manager == NULL)
753 		manager = timermgr;
754 	if (manager == NULL)
755 		return;
756 	TIME_NOW(&now);
757 	dispatch(manager, &now);
758 }
759 
760 isc_result_t
761 isc__timer_register(void) {
762 	return (isc_timer_register(isc__timermgr_create));
763 }
764 
765 static isc_timermgrcreatefunc_t timermgr_createfunc = NULL;
766 
767 isc_result_t
768 isc_timer_register(isc_timermgrcreatefunc_t createfunc) {
769 	isc_result_t result = ISC_R_SUCCESS;
770 
771 	if (timermgr_createfunc == NULL)
772 		timermgr_createfunc = createfunc;
773 	else
774 		result = ISC_R_EXISTS;
775 
776 	return (result);
777 }
778 
779 isc_result_t
780 isc_timermgr_createinctx(isc_appctx_t *actx,
781 			 isc_timermgr_t **managerp)
782 {
783 	isc_result_t result;
784 
785 	REQUIRE(timermgr_createfunc != NULL);
786 	result = (*timermgr_createfunc)(managerp);
787 
788 	if (result == ISC_R_SUCCESS)
789 		isc_appctx_settimermgr(actx, *managerp);
790 
791 	return (result);
792 }
793 
794 isc_result_t
795 isc_timermgr_create(isc_timermgr_t **managerp) {
796 	return (isc__timermgr_create(managerp));
797 }
798 
799 void
800 isc_timermgr_destroy(isc_timermgr_t **managerp) {
801 	REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp));
802 
803 	isc__timermgr_destroy(managerp);
804 
805 	ENSURE(*managerp == NULL);
806 }
807 
808 isc_result_t
809 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
810 		 const isc_time_t *expires, const interval_t *interval,
811 		 isc_task_t *task, isc_taskaction_t action, void *arg,
812 		 isc_timer_t **timerp)
813 {
814 	REQUIRE(ISCAPI_TIMERMGR_VALID(manager));
815 
816 	return (isc__timer_create(manager, type, expires, interval,
817 				  task, action, arg, timerp));
818 }
819 
820 void
821 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
822 	REQUIRE(ISCAPI_TIMER_VALID(timer));
823 	REQUIRE(timerp != NULL && *timerp == NULL);
824 
825 	isc__timer_attach(timer, timerp);
826 
827 	ENSURE(*timerp == timer);
828 }
829 
830 void
831 isc_timer_detach(isc_timer_t **timerp) {
832 	REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp));
833 
834 	isc__timer_detach(timerp);
835 
836 	ENSURE(*timerp == NULL);
837 }
838 
839 isc_result_t
840 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
841 		const isc_time_t *expires, const interval_t *interval,
842 		isc_boolean_t purge)
843 {
844 	REQUIRE(ISCAPI_TIMER_VALID(timer));
845 
846 	return (isc__timer_reset(timer, type, expires,
847 				 interval, purge));
848 }
849 
850 isc_result_t
851 isc_timer_touch(isc_timer_t *timer) {
852 	REQUIRE(ISCAPI_TIMER_VALID(timer));
853 
854 	return (isc__timer_touch(timer));
855 }
856