xref: /openbsd/usr.bin/dig/lib/isc/timer.c (revision 1fb015a8)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
17*1fb015a8Sflorian /* $Id: timer.c,v 1.25 2020/09/14 08:40:44 florian Exp $ */
185185a700Sflorian 
195185a700Sflorian /*! \file */
205185a700Sflorian 
214465bcfbSjsg #include <sys/time.h>
225185a700Sflorian #include <stdlib.h>
234465bcfbSjsg #include <time.h>
245185a700Sflorian #include <isc/heap.h>
255185a700Sflorian #include <isc/task.h>
265185a700Sflorian #include <isc/timer.h>
275185a700Sflorian #include <isc/util.h>
285185a700Sflorian 
295185a700Sflorian #include "timer_p.h"
305185a700Sflorian 
318b553854Sflorian struct isc_timer {
325185a700Sflorian 	/*! Not locked. */
338b553854Sflorian 	isc_timermgr_t *		manager;
345185a700Sflorian 	/*! Locked by timer lock. */
355185a700Sflorian 	unsigned int			references;
367238a213Sflorian 	struct timespec			idle;
375185a700Sflorian 	/*! Locked by manager lock. */
387238a213Sflorian 	struct timespec			interval;
395185a700Sflorian 	isc_task_t *			task;
405185a700Sflorian 	isc_taskaction_t		action;
415185a700Sflorian 	void *				arg;
425185a700Sflorian 	unsigned int			index;
437238a213Sflorian 	struct timespec			due;
448b553854Sflorian 	LINK(isc_timer_t)		link;
455185a700Sflorian };
465185a700Sflorian 
478b553854Sflorian struct isc_timermgr {
485185a700Sflorian 	/* Not locked. */
495185a700Sflorian 	/* Locked by manager lock. */
50*1fb015a8Sflorian 	int			done;
518b553854Sflorian 	LIST(isc_timer_t)		timers;
525185a700Sflorian 	unsigned int			nscheduled;
537238a213Sflorian 	struct timespec			due;
545185a700Sflorian 	unsigned int			refs;
555185a700Sflorian 	isc_heap_t *			heap;
565185a700Sflorian };
575185a700Sflorian 
585185a700Sflorian /*%
595185a700Sflorian  * The following are intended for internal use (indicated by "isc__"
605185a700Sflorian  * prefix) but are not declared as static, allowing direct access from
615185a700Sflorian  * unit tests etc.
625185a700Sflorian  */
635185a700Sflorian 
645185a700Sflorian /*!
655185a700Sflorian  * If the manager is supposed to be shared, there can be only one.
665185a700Sflorian  */
678b553854Sflorian static isc_timermgr_t *timermgr = NULL;
685185a700Sflorian 
695185a700Sflorian static inline isc_result_t
schedule(isc_timer_t * timer)70ad5cf538Sjung schedule(isc_timer_t *timer) {
715185a700Sflorian 	isc_result_t result;
728b553854Sflorian 	isc_timermgr_t *manager;
737238a213Sflorian 	struct timespec due;
745185a700Sflorian 
755185a700Sflorian 	/*!
765185a700Sflorian 	 * Note: the caller must ensure locking.
775185a700Sflorian 	 */
785185a700Sflorian 
795185a700Sflorian 	manager = timer->manager;
805185a700Sflorian 
815185a700Sflorian 	/*
825185a700Sflorian 	 * Compute the new due time.
835185a700Sflorian 	 */
845185a700Sflorian 	due = timer->idle;
855185a700Sflorian 
865185a700Sflorian 	/*
875185a700Sflorian 	 * Schedule the timer.
885185a700Sflorian 	 */
895185a700Sflorian 
905185a700Sflorian 	if (timer->index > 0) {
915185a700Sflorian 		/*
925185a700Sflorian 		 * Already scheduled.
935185a700Sflorian 		 */
94ba6f4614Sflorian 		if (timespeccmp(&due, &timer->due, <))
955185a700Sflorian 		    isc_heap_increased(manager->heap, timer->index);
96ba6f4614Sflorian 		else if (timespeccmp(&due, &timer->due, >))
975185a700Sflorian 		    isc_heap_decreased(manager->heap, timer->index);
98ba6f4614Sflorian 
99ba6f4614Sflorian 		timer->due = due;
1005185a700Sflorian 	} else {
1015185a700Sflorian 		timer->due = due;
1025185a700Sflorian 		result = isc_heap_insert(manager->heap, timer);
1035185a700Sflorian 		if (result != ISC_R_SUCCESS) {
1045185a700Sflorian 			INSIST(result == ISC_R_NOMEMORY);
1055185a700Sflorian 			return (ISC_R_NOMEMORY);
1065185a700Sflorian 		}
1075185a700Sflorian 		manager->nscheduled++;
1085185a700Sflorian 	}
1095185a700Sflorian 
1105185a700Sflorian 	/*
1115185a700Sflorian 	 * If this timer is at the head of the queue, we need to ensure
1125185a700Sflorian 	 * that we won't miss it if it has a more recent due time than
1135185a700Sflorian 	 * the current "next" timer.  We do this either by waking up the
1145185a700Sflorian 	 * run thread, or explicitly setting the value in the manager.
1155185a700Sflorian 	 */
116ba6f4614Sflorian 	if (timer->index == 1 && timespeccmp(&timer->due, &manager->due, <))
1175185a700Sflorian 		manager->due = timer->due;
1185185a700Sflorian 
1195185a700Sflorian 	return (ISC_R_SUCCESS);
1205185a700Sflorian }
1215185a700Sflorian 
1225185a700Sflorian static inline void
deschedule(isc_timer_t * timer)1238b553854Sflorian deschedule(isc_timer_t *timer) {
1248b553854Sflorian 	isc_timermgr_t *manager;
1255185a700Sflorian 
1265185a700Sflorian 	/*
1275185a700Sflorian 	 * The caller must ensure locking.
1285185a700Sflorian 	 */
1295185a700Sflorian 
1305185a700Sflorian 	manager = timer->manager;
1315185a700Sflorian 	if (timer->index > 0) {
1325185a700Sflorian 		isc_heap_delete(manager->heap, timer->index);
1335185a700Sflorian 		timer->index = 0;
1345185a700Sflorian 		INSIST(manager->nscheduled > 0);
1355185a700Sflorian 		manager->nscheduled--;
1365185a700Sflorian 	}
1375185a700Sflorian }
1385185a700Sflorian 
1395185a700Sflorian static void
destroy(isc_timer_t * timer)1408b553854Sflorian destroy(isc_timer_t *timer) {
1418b553854Sflorian 	isc_timermgr_t *manager = timer->manager;
1425185a700Sflorian 
1435185a700Sflorian 	/*
1445185a700Sflorian 	 * The caller must ensure it is safe to destroy the timer.
1455185a700Sflorian 	 */
1465185a700Sflorian 
1475185a700Sflorian 	(void)isc_task_purgerange(timer->task,
1485185a700Sflorian 				  timer,
1495185a700Sflorian 				  ISC_TIMEREVENT_FIRSTEVENT,
1505185a700Sflorian 				  ISC_TIMEREVENT_LASTEVENT,
1515185a700Sflorian 				  NULL);
1525185a700Sflorian 	deschedule(timer);
1535185a700Sflorian 	UNLINK(manager->timers, timer, link);
1545185a700Sflorian 
1555185a700Sflorian 	isc_task_detach(&timer->task);
1565185a700Sflorian 	free(timer);
1575185a700Sflorian }
1585185a700Sflorian 
1595185a700Sflorian isc_result_t
isc_timer_create(isc_timermgr_t * manager0,const struct timespec * interval,isc_task_t * task,isc_taskaction_t action,void * arg,isc_timer_t ** timerp)1608b553854Sflorian isc_timer_create(isc_timermgr_t *manager0, const struct timespec *interval,
1615185a700Sflorian 		  isc_task_t *task, isc_taskaction_t action, void *arg,
1625185a700Sflorian 		  isc_timer_t **timerp)
1635185a700Sflorian {
1648b553854Sflorian 	isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
1658b553854Sflorian 	isc_timer_t *timer;
1665185a700Sflorian 	isc_result_t result;
1677238a213Sflorian 	struct timespec now;
1685185a700Sflorian 
1695185a700Sflorian 	/*
1705185a700Sflorian 	 * Create a new 'type' timer managed by 'manager'.  The timers
1715ec36317Sflorian 	 * parameters are specified by 'interval'.  Events
1725185a700Sflorian 	 * will be posted to 'task' and when dispatched 'action' will be
1735185a700Sflorian 	 * called with 'arg' as the arg value.  The new timer is returned
1745185a700Sflorian 	 * in 'timerp'.
1755185a700Sflorian 	 */
1765185a700Sflorian 
1775185a700Sflorian 	REQUIRE(task != NULL);
1785185a700Sflorian 	REQUIRE(action != NULL);
1791eb893f3Sflorian 	REQUIRE(interval != NULL);
180396be909Sflorian 	REQUIRE(timespecisset(interval));
1815185a700Sflorian 	REQUIRE(timerp != NULL && *timerp == NULL);
1825185a700Sflorian 
1835185a700Sflorian 	/*
1845185a700Sflorian 	 * Get current time.
1855185a700Sflorian 	 */
186b53d8310Sflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
1875185a700Sflorian 
1885185a700Sflorian 	timer = malloc(sizeof(*timer));
1895185a700Sflorian 	if (timer == NULL)
1905185a700Sflorian 		return (ISC_R_NOMEMORY);
1915185a700Sflorian 
1925185a700Sflorian 	timer->manager = manager;
1935185a700Sflorian 	timer->references = 1;
1945185a700Sflorian 
195ffbbf1a1Sflorian 	if (timespecisset(interval))
196ffbbf1a1Sflorian 		timespecadd(&now, interval, &timer->idle);
1975185a700Sflorian 
1985185a700Sflorian 	timer->interval = *interval;
1995185a700Sflorian 	timer->task = NULL;
2005185a700Sflorian 	isc_task_attach(task, &timer->task);
2015185a700Sflorian 	timer->action = action;
2025185a700Sflorian 	/*
2035185a700Sflorian 	 * Removing the const attribute from "arg" is the best of two
2045185a700Sflorian 	 * evils here.  If the timer->arg member is made const, then
2055185a700Sflorian 	 * it affects a great many recipients of the timer event
2065185a700Sflorian 	 * which did not pass in an "arg" that was truly const.
2075185a700Sflorian 	 * Changing isc_timer_create() to not have "arg" prototyped as const,
2085185a700Sflorian 	 * though, can cause compilers warnings for calls that *do*
2095185a700Sflorian 	 * have a truly const arg.  The caller will have to carefully
2105185a700Sflorian 	 * keep track of whether arg started as a true const.
2115185a700Sflorian 	 */
2125185a700Sflorian 	DE_CONST(arg, timer->arg);
2135185a700Sflorian 	timer->index = 0;
2145185a700Sflorian 	ISC_LINK_INIT(timer, link);
2155185a700Sflorian 
216ad5cf538Sjung 	result = schedule(timer);
2175185a700Sflorian 	if (result == ISC_R_SUCCESS)
2185185a700Sflorian 		APPEND(manager->timers, timer, link);
2195185a700Sflorian 
2205185a700Sflorian 	if (result != ISC_R_SUCCESS) {
2215185a700Sflorian 		isc_task_detach(&timer->task);
2225185a700Sflorian 		free(timer);
2235185a700Sflorian 		return (result);
2245185a700Sflorian 	}
2255185a700Sflorian 
2265185a700Sflorian 	*timerp = (isc_timer_t *)timer;
2275185a700Sflorian 
2285185a700Sflorian 	return (ISC_R_SUCCESS);
2295185a700Sflorian }
2305185a700Sflorian 
2315185a700Sflorian isc_result_t
isc_timer_reset(isc_timer_t * timer,const struct timespec * interval,int purge)2328b553854Sflorian isc_timer_reset(isc_timer_t *timer, const struct timespec *interval,
233*1fb015a8Sflorian 		 int purge)
2345185a700Sflorian {
2357238a213Sflorian 	struct timespec now;
2365185a700Sflorian 	isc_result_t result;
2375185a700Sflorian 
2385185a700Sflorian 	/*
2395185a700Sflorian 	 * Change the timer's type, expires, and interval values to the given
240*1fb015a8Sflorian 	 * values.  If 'purge' is 1, any pending events from this timer
2415185a700Sflorian 	 * are purged from its task's event queue.
2425185a700Sflorian 	 */
2435185a700Sflorian 
2441eb893f3Sflorian 	REQUIRE(interval != NULL);
245396be909Sflorian 	REQUIRE(timespecisset(interval));
2465185a700Sflorian 
2475185a700Sflorian 	/*
2485185a700Sflorian 	 * Get current time.
2495185a700Sflorian 	 */
250b53d8310Sflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
2515185a700Sflorian 
2525185a700Sflorian 	if (purge)
2535185a700Sflorian 		(void)isc_task_purgerange(timer->task,
2545185a700Sflorian 					  timer,
2555185a700Sflorian 					  ISC_TIMEREVENT_FIRSTEVENT,
2565185a700Sflorian 					  ISC_TIMEREVENT_LASTEVENT,
2575185a700Sflorian 					  NULL);
2585185a700Sflorian 	timer->interval = *interval;
259396be909Sflorian 	if (timespecisset(interval)) {
260ffbbf1a1Sflorian 		timespecadd(&now, interval, &timer->idle);
2615185a700Sflorian 	} else {
262bb7ec108Sflorian 		timespecclear(&timer->idle);
2635185a700Sflorian 	}
2645185a700Sflorian 
265ad5cf538Sjung 	result = schedule(timer);
2665185a700Sflorian 
2675185a700Sflorian 	return (result);
2685185a700Sflorian }
2695185a700Sflorian 
270ffbbf1a1Sflorian void
isc_timer_touch(isc_timer_t * timer)2718b553854Sflorian isc_timer_touch(isc_timer_t *timer) {
2727238a213Sflorian 	struct timespec now;
2735185a700Sflorian 
2745185a700Sflorian 	/*
2755185a700Sflorian 	 * Set the last-touched time of 'timer' to the current time.
2765185a700Sflorian 	 */
2775185a700Sflorian 
278b53d8310Sflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
279ffbbf1a1Sflorian 	timespecadd(&now, &timer->interval, &timer->idle);
2805185a700Sflorian }
2815185a700Sflorian 
2825185a700Sflorian void
isc_timer_detach(isc_timer_t ** timerp)2838b553854Sflorian isc_timer_detach(isc_timer_t **timerp) {
2848b553854Sflorian 	isc_timer_t *timer;
285*1fb015a8Sflorian 	int free_timer = 0;
2865185a700Sflorian 
2875185a700Sflorian 	/*
2885185a700Sflorian 	 * Detach *timerp from its timer.
2895185a700Sflorian 	 */
2905185a700Sflorian 
2915185a700Sflorian 	REQUIRE(timerp != NULL);
2928b553854Sflorian 	timer = (isc_timer_t *)*timerp;
2935185a700Sflorian 
2945185a700Sflorian 	REQUIRE(timer->references > 0);
2955185a700Sflorian 	timer->references--;
2965185a700Sflorian 	if (timer->references == 0)
297*1fb015a8Sflorian 		free_timer = 1;
2985185a700Sflorian 
2995185a700Sflorian 	if (free_timer)
3005185a700Sflorian 		destroy(timer);
3015185a700Sflorian 
3025185a700Sflorian 	*timerp = NULL;
3035185a700Sflorian }
3045185a700Sflorian 
3055185a700Sflorian static void
dispatch(isc_timermgr_t * manager,struct timespec * now)3068b553854Sflorian dispatch(isc_timermgr_t *manager, struct timespec *now) {
307*1fb015a8Sflorian 	int done = 0, post_event, need_schedule;
3085185a700Sflorian 	isc_timerevent_t *event;
3095185a700Sflorian 	isc_eventtype_t type = 0;
3108b553854Sflorian 	isc_timer_t *timer;
3115185a700Sflorian 	isc_result_t result;
312*1fb015a8Sflorian 	int idle;
3135185a700Sflorian 
3145185a700Sflorian 	/*!
3155185a700Sflorian 	 * The caller must be holding the manager lock.
3165185a700Sflorian 	 */
3175185a700Sflorian 
3185185a700Sflorian 	while (manager->nscheduled > 0 && !done) {
3195185a700Sflorian 		timer = isc_heap_element(manager->heap, 1);
32075fe954bSflorian 		INSIST(timer != NULL);
321ba6f4614Sflorian 		if (timespeccmp(now, &timer->due, >=)) {
322*1fb015a8Sflorian 			idle = 0;
3235185a700Sflorian 
324ba6f4614Sflorian 			if (timespecisset(&timer->idle) && timespeccmp(now,
325ba6f4614Sflorian 			    &timer->idle, >=)) {
326*1fb015a8Sflorian 				idle = 1;
3275185a700Sflorian 			}
3285185a700Sflorian 			if (idle) {
3295185a700Sflorian 				type = ISC_TIMEREVENT_IDLE;
330*1fb015a8Sflorian 				post_event = 1;
331*1fb015a8Sflorian 				need_schedule = 0;
3325185a700Sflorian 			} else {
3335185a700Sflorian 				/*
3345185a700Sflorian 				 * Idle timer has been touched;
3355185a700Sflorian 				 * reschedule.
3365185a700Sflorian 				 */
337*1fb015a8Sflorian 				post_event = 0;
338*1fb015a8Sflorian 				need_schedule = 1;
3395185a700Sflorian 			}
3405185a700Sflorian 
3415185a700Sflorian 			if (post_event) {
3425185a700Sflorian 				/*
3435185a700Sflorian 				 * XXX We could preallocate this event.
3445185a700Sflorian 				 */
3455185a700Sflorian 				event = (isc_timerevent_t *)isc_event_allocate(
3465185a700Sflorian 							   timer,
3475185a700Sflorian 							   type,
3485185a700Sflorian 							   timer->action,
3495185a700Sflorian 							   timer->arg,
3505185a700Sflorian 							   sizeof(*event));
3515185a700Sflorian 
3525185a700Sflorian 				if (event != NULL) {
3535185a700Sflorian 					event->due = timer->due;
3545185a700Sflorian 					isc_task_send(timer->task,
3555185a700Sflorian 						      ISC_EVENT_PTR(&event));
3565185a700Sflorian 				} else
3575185a700Sflorian 					UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
3585185a700Sflorian 						 "couldn't allocate event");
3595185a700Sflorian 			}
3605185a700Sflorian 
3615185a700Sflorian 			timer->index = 0;
3625185a700Sflorian 			isc_heap_delete(manager->heap, 1);
3635185a700Sflorian 			manager->nscheduled--;
3645185a700Sflorian 
3655185a700Sflorian 			if (need_schedule) {
366ad5cf538Sjung 				result = schedule(timer);
3675185a700Sflorian 				if (result != ISC_R_SUCCESS)
3685185a700Sflorian 					UNEXPECTED_ERROR(__FILE__, __LINE__,
3695185a700Sflorian 						"%s: %u",
3705185a700Sflorian 						"couldn't schedule timer",
3715185a700Sflorian 						result);
3725185a700Sflorian 			}
3735185a700Sflorian 		} else {
3745185a700Sflorian 			manager->due = timer->due;
375*1fb015a8Sflorian 			done = 1;
3765185a700Sflorian 		}
3775185a700Sflorian 	}
3785185a700Sflorian }
3795185a700Sflorian 
380*1fb015a8Sflorian static int
sooner(void * v1,void * v2)3815185a700Sflorian sooner(void *v1, void *v2) {
3828b553854Sflorian 	isc_timer_t *t1, *t2;
3835185a700Sflorian 
3845185a700Sflorian 	t1 = v1;
3855185a700Sflorian 	t2 = v2;
3865185a700Sflorian 
387ba6f4614Sflorian 	if (timespeccmp(&t1->due, &t2->due, <))
388*1fb015a8Sflorian 		return (1);
389*1fb015a8Sflorian 	return (0);
3905185a700Sflorian }
3915185a700Sflorian 
3925185a700Sflorian static void
set_index(void * what,unsigned int index)3935185a700Sflorian set_index(void *what, unsigned int index) {
3948b553854Sflorian 	isc_timer_t *timer;
3955185a700Sflorian 
3965185a700Sflorian 	timer = what;
3975185a700Sflorian 
3985185a700Sflorian 	timer->index = index;
3995185a700Sflorian }
4005185a700Sflorian 
4015185a700Sflorian isc_result_t
isc_timermgr_create(isc_timermgr_t ** managerp)4028b553854Sflorian isc_timermgr_create(isc_timermgr_t **managerp) {
4038b553854Sflorian 	isc_timermgr_t *manager;
4045185a700Sflorian 	isc_result_t result;
4055185a700Sflorian 
4065185a700Sflorian 	/*
4075185a700Sflorian 	 * Create a timer manager.
4085185a700Sflorian 	 */
4095185a700Sflorian 
4105185a700Sflorian 	REQUIRE(managerp != NULL && *managerp == NULL);
4115185a700Sflorian 
4125185a700Sflorian 	if (timermgr != NULL) {
4135185a700Sflorian 		timermgr->refs++;
4145185a700Sflorian 		*managerp = (isc_timermgr_t *)timermgr;
4155185a700Sflorian 		return (ISC_R_SUCCESS);
4165185a700Sflorian 	}
4175185a700Sflorian 
4185185a700Sflorian 	manager = malloc(sizeof(*manager));
4195185a700Sflorian 	if (manager == NULL)
4205185a700Sflorian 		return (ISC_R_NOMEMORY);
4215185a700Sflorian 
422*1fb015a8Sflorian 	manager->done = 0;
4235185a700Sflorian 	INIT_LIST(manager->timers);
4245185a700Sflorian 	manager->nscheduled = 0;
425bb7ec108Sflorian 	timespecclear(&manager->due);
4265185a700Sflorian 	manager->heap = NULL;
4275185a700Sflorian 	result = isc_heap_create(sooner, set_index, 0, &manager->heap);
4285185a700Sflorian 	if (result != ISC_R_SUCCESS) {
4295185a700Sflorian 		INSIST(result == ISC_R_NOMEMORY);
4305185a700Sflorian 		free(manager);
4315185a700Sflorian 		return (ISC_R_NOMEMORY);
4325185a700Sflorian 	}
4335185a700Sflorian 	manager->refs = 1;
4345185a700Sflorian 	timermgr = manager;
4355185a700Sflorian 
4365185a700Sflorian 	*managerp = (isc_timermgr_t *)manager;
4375185a700Sflorian 
4385185a700Sflorian 	return (ISC_R_SUCCESS);
4395185a700Sflorian }
4405185a700Sflorian 
4415185a700Sflorian void
isc_timermgr_destroy(isc_timermgr_t ** managerp)4428b553854Sflorian isc_timermgr_destroy(isc_timermgr_t **managerp) {
4438b553854Sflorian 	isc_timermgr_t *manager;
4445185a700Sflorian 
4455185a700Sflorian 	/*
4465185a700Sflorian 	 * Destroy a timer manager.
4475185a700Sflorian 	 */
4485185a700Sflorian 
4495185a700Sflorian 	REQUIRE(managerp != NULL);
4508b553854Sflorian 	manager = (isc_timermgr_t *)*managerp;
4515185a700Sflorian 
4525185a700Sflorian 	manager->refs--;
4535185a700Sflorian 	if (manager->refs > 0) {
4545185a700Sflorian 		*managerp = NULL;
4555185a700Sflorian 		return;
4565185a700Sflorian 	}
4575185a700Sflorian 	timermgr = NULL;
4585185a700Sflorian 
4598b553854Sflorian 	isc_timermgr_dispatch((isc_timermgr_t *)manager);
4605185a700Sflorian 
4615185a700Sflorian 	REQUIRE(EMPTY(manager->timers));
462*1fb015a8Sflorian 	manager->done = 1;
4635185a700Sflorian 
4645185a700Sflorian 	/*
4655185a700Sflorian 	 * Clean up.
4665185a700Sflorian 	 */
4675185a700Sflorian 	isc_heap_destroy(&manager->heap);
4685185a700Sflorian 	free(manager);
4695185a700Sflorian 
4705185a700Sflorian 	*managerp = NULL;
4715185a700Sflorian 
4725185a700Sflorian 	timermgr = NULL;
4735185a700Sflorian }
4745185a700Sflorian 
4755185a700Sflorian isc_result_t
isc_timermgr_nextevent(isc_timermgr_t * manager0,struct timespec * when)4768b553854Sflorian isc_timermgr_nextevent(isc_timermgr_t *manager0, struct timespec *when) {
4778b553854Sflorian 	isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
4785185a700Sflorian 
4795185a700Sflorian 	if (manager == NULL)
4805185a700Sflorian 		manager = timermgr;
4815185a700Sflorian 	if (manager == NULL || manager->nscheduled == 0)
4825185a700Sflorian 		return (ISC_R_NOTFOUND);
4835185a700Sflorian 	*when = manager->due;
4845185a700Sflorian 	return (ISC_R_SUCCESS);
4855185a700Sflorian }
4865185a700Sflorian 
4875185a700Sflorian void
isc_timermgr_dispatch(isc_timermgr_t * manager0)4888b553854Sflorian isc_timermgr_dispatch(isc_timermgr_t *manager0) {
4898b553854Sflorian 	isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
4907238a213Sflorian 	struct timespec now;
4915185a700Sflorian 
4925185a700Sflorian 	if (manager == NULL)
4935185a700Sflorian 		manager = timermgr;
4945185a700Sflorian 	if (manager == NULL)
4955185a700Sflorian 		return;
496b53d8310Sflorian 	clock_gettime(CLOCK_MONOTONIC, &now);
4975185a700Sflorian 	dispatch(manager, &now);
4985185a700Sflorian }
4995185a700Sflorian 
500