Home | History | Annotate | Line # | Download | only in isc
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