Home | History | Annotate | Line # | Download | only in isc
timer.c revision 1.1.1.1
      1 /*	$NetBSD: timer.c,v 1.1.1.1 2018/08/12 12:08:23 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 <isc/app.h>
     20 #include <isc/condition.h>
     21 #include <isc/heap.h>
     22 #include <isc/log.h>
     23 #include <isc/magic.h>
     24 #include <isc/mem.h>
     25 #include <isc/msgs.h>
     26 #include <isc/once.h>
     27 #include <isc/platform.h>
     28 #include <isc/print.h>
     29 #include <isc/task.h>
     30 #include <isc/thread.h>
     31 #include <isc/time.h>
     32 #include <isc/timer.h>
     33 #include <isc/util.h>
     34 
     35 #ifdef OPENSSL_LEAKS
     36 #include <openssl/err.h>
     37 #endif
     38 
     39 /* See task.c about the following definition: */
     40 #ifdef ISC_PLATFORM_USETHREADS
     41 #define USE_TIMER_THREAD
     42 #else
     43 #define USE_SHARED_MANAGER
     44 #endif	/* ISC_PLATFORM_USETHREADS */
     45 
     46 #ifndef USE_TIMER_THREAD
     47 #include "timer_p.h"
     48 #endif /* USE_TIMER_THREAD */
     49 
     50 #ifdef ISC_TIMER_TRACE
     51 #define XTRACE(s)			fprintf(stderr, "%s\n", (s))
     52 #define XTRACEID(s, t)			fprintf(stderr, "%s %p\n", (s), (t))
     53 #define XTRACETIME(s, d)		fprintf(stderr, "%s %u.%09u\n", (s), \
     54 					       (d).seconds, (d).nanoseconds)
     55 #define XTRACETIME2(s, d, n)		fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
     56 					       (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
     57 #define XTRACETIMER(s, t, d)		fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
     58 					       (d).seconds, (d).nanoseconds)
     59 #else
     60 #define XTRACE(s)
     61 #define XTRACEID(s, t)
     62 #define XTRACETIME(s, d)
     63 #define XTRACETIME2(s, d, n)
     64 #define XTRACETIMER(s, t, d)
     65 #endif /* ISC_TIMER_TRACE */
     66 
     67 #define TIMER_MAGIC			ISC_MAGIC('T', 'I', 'M', 'R')
     68 #define VALID_TIMER(t)			ISC_MAGIC_VALID(t, TIMER_MAGIC)
     69 
     70 typedef struct isc__timer isc__timer_t;
     71 typedef struct isc__timermgr isc__timermgr_t;
     72 
     73 struct isc__timer {
     74 	/*! Not locked. */
     75 	isc_timer_t			common;
     76 	isc__timermgr_t *		manager;
     77 	isc_mutex_t			lock;
     78 	/*! Locked by timer lock. */
     79 	unsigned int			references;
     80 	isc_time_t			idle;
     81 	/*! Locked by manager lock. */
     82 	isc_timertype_t			type;
     83 	isc_time_t			expires;
     84 	isc_interval_t			interval;
     85 	isc_task_t *			task;
     86 	isc_taskaction_t		action;
     87 	void *				arg;
     88 	unsigned int			index;
     89 	isc_time_t			due;
     90 	LINK(isc__timer_t)		link;
     91 };
     92 
     93 #define TIMER_MANAGER_MAGIC		ISC_MAGIC('T', 'I', 'M', 'M')
     94 #define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
     95 
     96 struct isc__timermgr {
     97 	/* Not locked. */
     98 	isc_timermgr_t			common;
     99 	isc_mem_t *			mctx;
    100 	isc_mutex_t			lock;
    101 	/* Locked by manager lock. */
    102 	isc_boolean_t			done;
    103 	LIST(isc__timer_t)		timers;
    104 	unsigned int			nscheduled;
    105 	isc_time_t			due;
    106 #ifdef USE_TIMER_THREAD
    107 	isc_condition_t			wakeup;
    108 	isc_thread_t			thread;
    109 #endif	/* USE_TIMER_THREAD */
    110 #ifdef USE_SHARED_MANAGER
    111 	unsigned int			refs;
    112 #endif /* USE_SHARED_MANAGER */
    113 	isc_heap_t *			heap;
    114 };
    115 
    116 /*%
    117  * The following are intended for internal use (indicated by "isc__"
    118  * prefix) but are not declared as static, allowing direct access from
    119  * unit tests etc.
    120  */
    121 
    122 isc_result_t
    123 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
    124 		  const isc_time_t *expires, const isc_interval_t *interval,
    125 		  isc_task_t *task, isc_taskaction_t action, void *arg,
    126 		  isc_timer_t **timerp);
    127 isc_result_t
    128 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
    129 		 const isc_time_t *expires, const isc_interval_t *interval,
    130 		 isc_boolean_t purge);
    131 isc_timertype_t
    132 isc_timer_gettype(isc_timer_t *timer);
    133 isc_result_t
    134 isc__timer_touch(isc_timer_t *timer);
    135 void
    136 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
    137 void
    138 isc__timer_detach(isc_timer_t **timerp);
    139 isc_result_t
    140 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
    141 void
    142 isc_timermgr_poke(isc_timermgr_t *manager0);
    143 void
    144 isc__timermgr_destroy(isc_timermgr_t **managerp);
    145 
    146 static struct isc__timermethods {
    147 	isc_timermethods_t methods;
    148 
    149 	/*%
    150 	 * The following are defined just for avoiding unused static functions.
    151 	 */
    152 	void *gettype;
    153 } timermethods = {
    154 	{
    155 		isc__timer_attach,
    156 		isc__timer_detach,
    157 		isc__timer_reset,
    158 		isc__timer_touch
    159 	},
    160 	(void *)isc_timer_gettype
    161 };
    162 
    163 static struct isc__timermgrmethods {
    164 	isc_timermgrmethods_t methods;
    165 	void *poke;		/* see above */
    166 } timermgrmethods = {
    167 	{
    168 		isc__timermgr_destroy,
    169 		isc__timer_create
    170 	},
    171 	(void *)isc_timermgr_poke
    172 };
    173 
    174 #ifdef USE_SHARED_MANAGER
    175 /*!
    176  * If the manager is supposed to be shared, there can be only one.
    177  */
    178 static isc__timermgr_t *timermgr = NULL;
    179 #endif /* USE_SHARED_MANAGER */
    180 
    181 static inline isc_result_t
    182 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
    183 	isc_result_t result;
    184 	isc__timermgr_t *manager;
    185 	isc_time_t due;
    186 	int cmp;
    187 #ifdef USE_TIMER_THREAD
    188 	isc_boolean_t timedwait;
    189 #endif
    190 
    191 	/*!
    192 	 * Note: the caller must ensure locking.
    193 	 */
    194 
    195 	REQUIRE(timer->type != isc_timertype_inactive);
    196 
    197 #ifndef USE_TIMER_THREAD
    198 	UNUSED(signal_ok);
    199 #endif /* USE_TIMER_THREAD */
    200 
    201 	manager = timer->manager;
    202 
    203 #ifdef USE_TIMER_THREAD
    204 	/*!
    205 	 * If the manager was timed wait, we may need to signal the
    206 	 * manager to force a wakeup.
    207 	 */
    208 	timedwait = ISC_TF(manager->nscheduled > 0 &&
    209 			   isc_time_seconds(&manager->due) != 0);
    210 #endif
    211 
    212 	/*
    213 	 * Compute the new due time.
    214 	 */
    215 	if (timer->type != isc_timertype_once) {
    216 		result = isc_time_add(now, &timer->interval, &due);
    217 		if (result != ISC_R_SUCCESS)
    218 			return (result);
    219 		if (timer->type == isc_timertype_limited &&
    220 		    isc_time_compare(&timer->expires, &due) < 0)
    221 			due = timer->expires;
    222 	} else {
    223 		if (isc_time_isepoch(&timer->idle))
    224 			due = timer->expires;
    225 		else if (isc_time_isepoch(&timer->expires))
    226 			due = timer->idle;
    227 		else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
    228 			due = timer->idle;
    229 		else
    230 			due = timer->expires;
    231 	}
    232 
    233 	/*
    234 	 * Schedule the timer.
    235 	 */
    236 
    237 	if (timer->index > 0) {
    238 		/*
    239 		 * Already scheduled.
    240 		 */
    241 		cmp = isc_time_compare(&due, &timer->due);
    242 		timer->due = due;
    243 		switch (cmp) {
    244 		case -1:
    245 			isc_heap_increased(manager->heap, timer->index);
    246 			break;
    247 		case 1:
    248 			isc_heap_decreased(manager->heap, timer->index);
    249 			break;
    250 		case 0:
    251 			/* Nothing to do. */
    252 			break;
    253 		}
    254 	} else {
    255 		timer->due = due;
    256 		result = isc_heap_insert(manager->heap, timer);
    257 		if (result != ISC_R_SUCCESS) {
    258 			INSIST(result == ISC_R_NOMEMORY);
    259 			return (ISC_R_NOMEMORY);
    260 		}
    261 		manager->nscheduled++;
    262 	}
    263 
    264 	XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
    265 				   ISC_MSG_SCHEDULE, "schedule"), timer, due);
    266 
    267 	/*
    268 	 * If this timer is at the head of the queue, we need to ensure
    269 	 * that we won't miss it if it has a more recent due time than
    270 	 * the current "next" timer.  We do this either by waking up the
    271 	 * run thread, or explicitly setting the value in the manager.
    272 	 */
    273 #ifdef USE_TIMER_THREAD
    274 
    275 	/*
    276 	 * This is a temporary (probably) hack to fix a bug on tru64 5.1
    277 	 * and 5.1a.  Sometimes, pthread_cond_timedwait() doesn't actually
    278 	 * return when the time expires, so here, we check to see if
    279 	 * we're 15 seconds or more behind, and if we are, we signal
    280 	 * the dispatcher.  This isn't such a bad idea as a general purpose
    281 	 * watchdog, so perhaps we should just leave it in here.
    282 	 */
    283 	if (signal_ok && timedwait) {
    284 		isc_interval_t fifteen;
    285 		isc_time_t then;
    286 
    287 		isc_interval_set(&fifteen, 15, 0);
    288 		result = isc_time_add(&manager->due, &fifteen, &then);
    289 
    290 		if (result == ISC_R_SUCCESS &&
    291 		    isc_time_compare(&then, now) < 0) {
    292 			SIGNAL(&manager->wakeup);
    293 			signal_ok = ISC_FALSE;
    294 			isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    295 				      ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
    296 				      "*** POKED TIMER ***");
    297 		}
    298 	}
    299 
    300 	if (timer->index == 1 && signal_ok) {
    301 		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
    302 				      ISC_MSG_SIGNALSCHED,
    303 				      "signal (schedule)"));
    304 		SIGNAL(&manager->wakeup);
    305 	}
    306 #else /* USE_TIMER_THREAD */
    307 	if (timer->index == 1 &&
    308 	    isc_time_compare(&timer->due, &manager->due) < 0)
    309 		manager->due = timer->due;
    310 #endif /* USE_TIMER_THREAD */
    311 
    312 	return (ISC_R_SUCCESS);
    313 }
    314 
    315 static inline void
    316 deschedule(isc__timer_t *timer) {
    317 #ifdef USE_TIMER_THREAD
    318 	isc_boolean_t need_wakeup = ISC_FALSE;
    319 #endif
    320 	isc__timermgr_t *manager;
    321 
    322 	/*
    323 	 * The caller must ensure locking.
    324 	 */
    325 
    326 	manager = timer->manager;
    327 	if (timer->index > 0) {
    328 #ifdef USE_TIMER_THREAD
    329 		if (timer->index == 1)
    330 			need_wakeup = ISC_TRUE;
    331 #endif
    332 		isc_heap_delete(manager->heap, timer->index);
    333 		timer->index = 0;
    334 		INSIST(manager->nscheduled > 0);
    335 		manager->nscheduled--;
    336 #ifdef USE_TIMER_THREAD
    337 		if (need_wakeup) {
    338 			XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
    339 					      ISC_MSG_SIGNALDESCHED,
    340 					      "signal (deschedule)"));
    341 			SIGNAL(&manager->wakeup);
    342 		}
    343 #endif /* USE_TIMER_THREAD */
    344 	}
    345 }
    346 
    347 static void
    348 destroy(isc__timer_t *timer) {
    349 	isc__timermgr_t *manager = timer->manager;
    350 
    351 	/*
    352 	 * The caller must ensure it is safe to destroy the timer.
    353 	 */
    354 
    355 	LOCK(&manager->lock);
    356 
    357 	(void)isc_task_purgerange(timer->task,
    358 				  timer,
    359 				  ISC_TIMEREVENT_FIRSTEVENT,
    360 				  ISC_TIMEREVENT_LASTEVENT,
    361 				  NULL);
    362 	deschedule(timer);
    363 	UNLINK(manager->timers, timer, link);
    364 
    365 	UNLOCK(&manager->lock);
    366 
    367 	isc_task_detach(&timer->task);
    368 	DESTROYLOCK(&timer->lock);
    369 	timer->common.impmagic = 0;
    370 	timer->common.magic = 0;
    371 	isc_mem_put(manager->mctx, timer, sizeof(*timer));
    372 }
    373 
    374 isc_result_t
    375 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
    376 		  const isc_time_t *expires, const isc_interval_t *interval,
    377 		  isc_task_t *task, isc_taskaction_t action, void *arg,
    378 		  isc_timer_t **timerp)
    379 {
    380 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
    381 	isc__timer_t *timer;
    382 	isc_result_t result;
    383 	isc_time_t now;
    384 
    385 	/*
    386 	 * Create a new 'type' timer managed by 'manager'.  The timers
    387 	 * parameters are specified by 'expires' and 'interval'.  Events
    388 	 * will be posted to 'task' and when dispatched 'action' will be
    389 	 * called with 'arg' as the arg value.  The new timer is returned
    390 	 * in 'timerp'.
    391 	 */
    392 
    393 	REQUIRE(VALID_MANAGER(manager));
    394 	REQUIRE(task != NULL);
    395 	REQUIRE(action != NULL);
    396 	if (expires == NULL)
    397 		expires = isc_time_epoch;
    398 	if (interval == NULL)
    399 		interval = isc_interval_zero;
    400 	REQUIRE(type == isc_timertype_inactive ||
    401 		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
    402 	REQUIRE(timerp != NULL && *timerp == NULL);
    403 	REQUIRE(type != isc_timertype_limited ||
    404 		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
    405 
    406 	/*
    407 	 * Get current time.
    408 	 */
    409 	if (type != isc_timertype_inactive) {
    410 		TIME_NOW(&now);
    411 	} else {
    412 		/*
    413 		 * We don't have to do this, but it keeps the compiler from
    414 		 * complaining about "now" possibly being used without being
    415 		 * set, even though it will never actually happen.
    416 		 */
    417 		isc_time_settoepoch(&now);
    418 	}
    419 
    420 
    421 	timer = isc_mem_get(manager->mctx, sizeof(*timer));
    422 	if (timer == NULL)
    423 		return (ISC_R_NOMEMORY);
    424 
    425 	timer->manager = manager;
    426 	timer->references = 1;
    427 
    428 	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
    429 		result = isc_time_add(&now, interval, &timer->idle);
    430 		if (result != ISC_R_SUCCESS) {
    431 			isc_mem_put(manager->mctx, timer, sizeof(*timer));
    432 			return (result);
    433 		}
    434 	} else
    435 		isc_time_settoepoch(&timer->idle);
    436 
    437 	timer->type = type;
    438 	timer->expires = *expires;
    439 	timer->interval = *interval;
    440 	timer->task = NULL;
    441 	isc_task_attach(task, &timer->task);
    442 	timer->action = action;
    443 	/*
    444 	 * Removing the const attribute from "arg" is the best of two
    445 	 * evils here.  If the timer->arg member is made const, then
    446 	 * it affects a great many recipients of the timer event
    447 	 * which did not pass in an "arg" that was truly const.
    448 	 * Changing isc_timer_create() to not have "arg" prototyped as const,
    449 	 * though, can cause compilers warnings for calls that *do*
    450 	 * have a truly const arg.  The caller will have to carefully
    451 	 * keep track of whether arg started as a true const.
    452 	 */
    453 	DE_CONST(arg, timer->arg);
    454 	timer->index = 0;
    455 	result = isc_mutex_init(&timer->lock);
    456 	if (result != ISC_R_SUCCESS) {
    457 		isc_task_detach(&timer->task);
    458 		isc_mem_put(manager->mctx, timer, sizeof(*timer));
    459 		return (result);
    460 	}
    461 	ISC_LINK_INIT(timer, link);
    462 	timer->common.impmagic = TIMER_MAGIC;
    463 	timer->common.magic = ISCAPI_TIMER_MAGIC;
    464 	timer->common.methods = (isc_timermethods_t *)&timermethods;
    465 
    466 	LOCK(&manager->lock);
    467 
    468 	/*
    469 	 * Note we don't have to lock the timer like we normally would because
    470 	 * there are no external references to it yet.
    471 	 */
    472 
    473 	if (type != isc_timertype_inactive)
    474 		result = schedule(timer, &now, ISC_TRUE);
    475 	else
    476 		result = ISC_R_SUCCESS;
    477 	if (result == ISC_R_SUCCESS)
    478 		APPEND(manager->timers, timer, link);
    479 
    480 	UNLOCK(&manager->lock);
    481 
    482 	if (result != ISC_R_SUCCESS) {
    483 		timer->common.impmagic = 0;
    484 		timer->common.magic = 0;
    485 		DESTROYLOCK(&timer->lock);
    486 		isc_task_detach(&timer->task);
    487 		isc_mem_put(manager->mctx, timer, sizeof(*timer));
    488 		return (result);
    489 	}
    490 
    491 	*timerp = (isc_timer_t *)timer;
    492 
    493 	return (ISC_R_SUCCESS);
    494 }
    495 
    496 isc_result_t
    497 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
    498 		 const isc_time_t *expires, const isc_interval_t *interval,
    499 		 isc_boolean_t purge)
    500 {
    501 	isc__timer_t *timer = (isc__timer_t *)timer0;
    502 	isc_time_t now;
    503 	isc__timermgr_t *manager;
    504 	isc_result_t result;
    505 
    506 	/*
    507 	 * Change the timer's type, expires, and interval values to the given
    508 	 * values.  If 'purge' is ISC_TRUE, any pending events from this timer
    509 	 * are purged from its task's event queue.
    510 	 */
    511 
    512 	REQUIRE(VALID_TIMER(timer));
    513 	manager = timer->manager;
    514 	REQUIRE(VALID_MANAGER(manager));
    515 
    516 	if (expires == NULL)
    517 		expires = isc_time_epoch;
    518 	if (interval == NULL)
    519 		interval = isc_interval_zero;
    520 	REQUIRE(type == isc_timertype_inactive ||
    521 		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
    522 	REQUIRE(type != isc_timertype_limited ||
    523 		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
    524 
    525 	/*
    526 	 * Get current time.
    527 	 */
    528 	if (type != isc_timertype_inactive) {
    529 		TIME_NOW(&now);
    530 	} else {
    531 		/*
    532 		 * We don't have to do this, but it keeps the compiler from
    533 		 * complaining about "now" possibly being used without being
    534 		 * set, even though it will never actually happen.
    535 		 */
    536 		isc_time_settoepoch(&now);
    537 	}
    538 
    539 	LOCK(&manager->lock);
    540 	LOCK(&timer->lock);
    541 
    542 	if (purge)
    543 		(void)isc_task_purgerange(timer->task,
    544 					  timer,
    545 					  ISC_TIMEREVENT_FIRSTEVENT,
    546 					  ISC_TIMEREVENT_LASTEVENT,
    547 					  NULL);
    548 	timer->type = type;
    549 	timer->expires = *expires;
    550 	timer->interval = *interval;
    551 	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
    552 		result = isc_time_add(&now, interval, &timer->idle);
    553 	} else {
    554 		isc_time_settoepoch(&timer->idle);
    555 		result = ISC_R_SUCCESS;
    556 	}
    557 
    558 	if (result == ISC_R_SUCCESS) {
    559 		if (type == isc_timertype_inactive) {
    560 			deschedule(timer);
    561 			result = ISC_R_SUCCESS;
    562 		} else
    563 			result = schedule(timer, &now, ISC_TRUE);
    564 	}
    565 
    566 	UNLOCK(&timer->lock);
    567 	UNLOCK(&manager->lock);
    568 
    569 	return (result);
    570 }
    571 
    572 isc_timertype_t
    573 isc_timer_gettype(isc_timer_t *timer0) {
    574 	isc__timer_t *timer = (isc__timer_t *)timer0;
    575 	isc_timertype_t t;
    576 
    577 	REQUIRE(VALID_TIMER(timer));
    578 
    579 	LOCK(&timer->lock);
    580 	t = timer->type;
    581 	UNLOCK(&timer->lock);
    582 
    583 	return (t);
    584 }
    585 
    586 isc_result_t
    587 isc__timer_touch(isc_timer_t *timer0) {
    588 	isc__timer_t *timer = (isc__timer_t *)timer0;
    589 	isc_result_t result;
    590 	isc_time_t now;
    591 
    592 	/*
    593 	 * Set the last-touched time of 'timer' to the current time.
    594 	 */
    595 
    596 	REQUIRE(VALID_TIMER(timer));
    597 
    598 	LOCK(&timer->lock);
    599 
    600 	/*
    601 	 * We'd like to
    602 	 *
    603 	 *	REQUIRE(timer->type == isc_timertype_once);
    604 	 *
    605 	 * but we cannot without locking the manager lock too, which we
    606 	 * don't want to do.
    607 	 */
    608 
    609 	TIME_NOW(&now);
    610 	result = isc_time_add(&now, &timer->interval, &timer->idle);
    611 
    612 	UNLOCK(&timer->lock);
    613 
    614 	return (result);
    615 }
    616 
    617 void
    618 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
    619 	isc__timer_t *timer = (isc__timer_t *)timer0;
    620 
    621 	/*
    622 	 * Attach *timerp to timer.
    623 	 */
    624 
    625 	REQUIRE(VALID_TIMER(timer));
    626 	REQUIRE(timerp != NULL && *timerp == NULL);
    627 
    628 	LOCK(&timer->lock);
    629 	timer->references++;
    630 	UNLOCK(&timer->lock);
    631 
    632 	*timerp = (isc_timer_t *)timer;
    633 }
    634 
    635 void
    636 isc__timer_detach(isc_timer_t **timerp) {
    637 	isc__timer_t *timer;
    638 	isc_boolean_t free_timer = ISC_FALSE;
    639 
    640 	/*
    641 	 * Detach *timerp from its timer.
    642 	 */
    643 
    644 	REQUIRE(timerp != NULL);
    645 	timer = (isc__timer_t *)*timerp;
    646 	REQUIRE(VALID_TIMER(timer));
    647 
    648 	LOCK(&timer->lock);
    649 	REQUIRE(timer->references > 0);
    650 	timer->references--;
    651 	if (timer->references == 0)
    652 		free_timer = ISC_TRUE;
    653 	UNLOCK(&timer->lock);
    654 
    655 	if (free_timer)
    656 		destroy(timer);
    657 
    658 	*timerp = NULL;
    659 }
    660 
    661 static void
    662 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
    663 	isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
    664 	isc_timerevent_t *event;
    665 	isc_eventtype_t type = 0;
    666 	isc__timer_t *timer;
    667 	isc_result_t result;
    668 	isc_boolean_t idle;
    669 
    670 	/*!
    671 	 * The caller must be holding the manager lock.
    672 	 */
    673 
    674 	while (manager->nscheduled > 0 && !done) {
    675 		timer = isc_heap_element(manager->heap, 1);
    676 		INSIST(timer != NULL && timer->type != isc_timertype_inactive);
    677 		if (isc_time_compare(now, &timer->due) >= 0) {
    678 			if (timer->type == isc_timertype_ticker) {
    679 				type = ISC_TIMEREVENT_TICK;
    680 				post_event = ISC_TRUE;
    681 				need_schedule = ISC_TRUE;
    682 			} else if (timer->type == isc_timertype_limited) {
    683 				int cmp;
    684 				cmp = isc_time_compare(now, &timer->expires);
    685 				if (cmp >= 0) {
    686 					type = ISC_TIMEREVENT_LIFE;
    687 					post_event = ISC_TRUE;
    688 					need_schedule = ISC_FALSE;
    689 				} else {
    690 					type = ISC_TIMEREVENT_TICK;
    691 					post_event = ISC_TRUE;
    692 					need_schedule = ISC_TRUE;
    693 				}
    694 			} else if (!isc_time_isepoch(&timer->expires) &&
    695 				   isc_time_compare(now,
    696 						    &timer->expires) >= 0) {
    697 				type = ISC_TIMEREVENT_LIFE;
    698 				post_event = ISC_TRUE;
    699 				need_schedule = ISC_FALSE;
    700 			} else {
    701 				idle = ISC_FALSE;
    702 
    703 				LOCK(&timer->lock);
    704 				if (!isc_time_isepoch(&timer->idle) &&
    705 				    isc_time_compare(now,
    706 						     &timer->idle) >= 0) {
    707 					idle = ISC_TRUE;
    708 				}
    709 				UNLOCK(&timer->lock);
    710 				if (idle) {
    711 					type = ISC_TIMEREVENT_IDLE;
    712 					post_event = ISC_TRUE;
    713 					need_schedule = ISC_FALSE;
    714 				} else {
    715 					/*
    716 					 * Idle timer has been touched;
    717 					 * reschedule.
    718 					 */
    719 					XTRACEID(isc_msgcat_get(isc_msgcat,
    720 								ISC_MSGSET_TIMER,
    721 								ISC_MSG_IDLERESCHED,
    722 								"idle reschedule"),
    723 						 timer);
    724 					post_event = ISC_FALSE;
    725 					need_schedule = ISC_TRUE;
    726 				}
    727 			}
    728 
    729 			if (post_event) {
    730 				XTRACEID(isc_msgcat_get(isc_msgcat,
    731 							ISC_MSGSET_TIMER,
    732 							ISC_MSG_POSTING,
    733 							"posting"), timer);
    734 				/*
    735 				 * XXX We could preallocate this event.
    736 				 */
    737 				event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
    738 							   timer,
    739 							   type,
    740 							   timer->action,
    741 							   timer->arg,
    742 							   sizeof(*event));
    743 
    744 				if (event != NULL) {
    745 					event->due = timer->due;
    746 					isc_task_send(timer->task,
    747 						      ISC_EVENT_PTR(&event));
    748 				} else
    749 					UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
    750 						 isc_msgcat_get(isc_msgcat,
    751 							 ISC_MSGSET_TIMER,
    752 							 ISC_MSG_EVENTNOTALLOC,
    753 							 "couldn't "
    754 							 "allocate event"));
    755 			}
    756 
    757 			timer->index = 0;
    758 			isc_heap_delete(manager->heap, 1);
    759 			manager->nscheduled--;
    760 
    761 			if (need_schedule) {
    762 				result = schedule(timer, now, ISC_FALSE);
    763 				if (result != ISC_R_SUCCESS)
    764 					UNEXPECTED_ERROR(__FILE__, __LINE__,
    765 							 "%s: %u",
    766 						isc_msgcat_get(isc_msgcat,
    767 							ISC_MSGSET_TIMER,
    768 							ISC_MSG_SCHEDFAIL,
    769 							"couldn't schedule "
    770 							"timer"),
    771 							 result);
    772 			}
    773 		} else {
    774 			manager->due = timer->due;
    775 			done = ISC_TRUE;
    776 		}
    777 	}
    778 }
    779 
    780 #ifdef USE_TIMER_THREAD
    781 static isc_threadresult_t
    782 #ifdef _WIN32			/* XXXDCL */
    783 WINAPI
    784 #endif
    785 run(void *uap) {
    786 	isc__timermgr_t *manager = uap;
    787 	isc_time_t now;
    788 	isc_result_t result;
    789 
    790 	LOCK(&manager->lock);
    791 	while (!manager->done) {
    792 		TIME_NOW(&now);
    793 
    794 		XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
    795 					  ISC_MSG_RUNNING,
    796 					  "running"), now);
    797 
    798 		dispatch(manager, &now);
    799 
    800 		if (manager->nscheduled > 0) {
    801 			XTRACETIME2(isc_msgcat_get(isc_msgcat,
    802 						   ISC_MSGSET_GENERAL,
    803 						   ISC_MSG_WAITUNTIL,
    804 						   "waituntil"),
    805 				    manager->due, now);
    806 			result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
    807 			INSIST(result == ISC_R_SUCCESS ||
    808 			       result == ISC_R_TIMEDOUT);
    809 		} else {
    810 			XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
    811 						  ISC_MSG_WAIT, "wait"), now);
    812 			WAIT(&manager->wakeup, &manager->lock);
    813 		}
    814 		XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
    815 				      ISC_MSG_WAKEUP, "wakeup"));
    816 	}
    817 	UNLOCK(&manager->lock);
    818 
    819 #ifdef OPENSSL_LEAKS
    820 	ERR_remove_state(0);
    821 #endif
    822 
    823 	return ((isc_threadresult_t)0);
    824 }
    825 #endif /* USE_TIMER_THREAD */
    826 
    827 static isc_boolean_t
    828 sooner(void *v1, void *v2) {
    829 	isc__timer_t *t1, *t2;
    830 
    831 	t1 = v1;
    832 	t2 = v2;
    833 	REQUIRE(VALID_TIMER(t1));
    834 	REQUIRE(VALID_TIMER(t2));
    835 
    836 	if (isc_time_compare(&t1->due, &t2->due) < 0)
    837 		return (ISC_TRUE);
    838 	return (ISC_FALSE);
    839 }
    840 
    841 static void
    842 set_index(void *what, unsigned int index) {
    843 	isc__timer_t *timer;
    844 
    845 	timer = what;
    846 	REQUIRE(VALID_TIMER(timer));
    847 
    848 	timer->index = index;
    849 }
    850 
    851 isc_result_t
    852 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
    853 	isc__timermgr_t *manager;
    854 	isc_result_t result;
    855 
    856 	/*
    857 	 * Create a timer manager.
    858 	 */
    859 
    860 	REQUIRE(managerp != NULL && *managerp == NULL);
    861 
    862 #ifdef USE_SHARED_MANAGER
    863 	if (timermgr != NULL) {
    864 		timermgr->refs++;
    865 		*managerp = (isc_timermgr_t *)timermgr;
    866 		return (ISC_R_SUCCESS);
    867 	}
    868 #endif /* USE_SHARED_MANAGER */
    869 
    870 	manager = isc_mem_get(mctx, sizeof(*manager));
    871 	if (manager == NULL)
    872 		return (ISC_R_NOMEMORY);
    873 
    874 	manager->common.impmagic = TIMER_MANAGER_MAGIC;
    875 	manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
    876 	manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
    877 	manager->mctx = NULL;
    878 	manager->done = ISC_FALSE;
    879 	INIT_LIST(manager->timers);
    880 	manager->nscheduled = 0;
    881 	isc_time_settoepoch(&manager->due);
    882 	manager->heap = NULL;
    883 	result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
    884 	if (result != ISC_R_SUCCESS) {
    885 		INSIST(result == ISC_R_NOMEMORY);
    886 		isc_mem_put(mctx, manager, sizeof(*manager));
    887 		return (ISC_R_NOMEMORY);
    888 	}
    889 	result = isc_mutex_init(&manager->lock);
    890 	if (result != ISC_R_SUCCESS) {
    891 		isc_heap_destroy(&manager->heap);
    892 		isc_mem_put(mctx, manager, sizeof(*manager));
    893 		return (result);
    894 	}
    895 	isc_mem_attach(mctx, &manager->mctx);
    896 #ifdef USE_TIMER_THREAD
    897 	if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
    898 		isc_mem_detach(&manager->mctx);
    899 		DESTROYLOCK(&manager->lock);
    900 		isc_heap_destroy(&manager->heap);
    901 		isc_mem_put(mctx, manager, sizeof(*manager));
    902 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    903 				 "isc_condition_init() %s",
    904 				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
    905 						ISC_MSG_FAILED, "failed"));
    906 		return (ISC_R_UNEXPECTED);
    907 	}
    908 	if (isc_thread_create(run, manager, &manager->thread) !=
    909 	    ISC_R_SUCCESS) {
    910 		isc_mem_detach(&manager->mctx);
    911 		(void)isc_condition_destroy(&manager->wakeup);
    912 		DESTROYLOCK(&manager->lock);
    913 		isc_heap_destroy(&manager->heap);
    914 		isc_mem_put(mctx, manager, sizeof(*manager));
    915 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    916 				 "isc_thread_create() %s",
    917 				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
    918 						ISC_MSG_FAILED, "failed"));
    919 		return (ISC_R_UNEXPECTED);
    920 	}
    921 	isc_thread_setname(manager->thread, "isc-timer");
    922 #endif
    923 #ifdef USE_SHARED_MANAGER
    924 	manager->refs = 1;
    925 	timermgr = manager;
    926 #endif /* USE_SHARED_MANAGER */
    927 
    928 	*managerp = (isc_timermgr_t *)manager;
    929 
    930 	return (ISC_R_SUCCESS);
    931 }
    932 
    933 void
    934 isc_timermgr_poke(isc_timermgr_t *manager0) {
    935 #ifdef USE_TIMER_THREAD
    936 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
    937 
    938 	REQUIRE(VALID_MANAGER(manager));
    939 
    940 	SIGNAL(&manager->wakeup);
    941 #else
    942 	UNUSED(manager0);
    943 #endif
    944 }
    945 
    946 void
    947 isc__timermgr_destroy(isc_timermgr_t **managerp) {
    948 	isc__timermgr_t *manager;
    949 	isc_mem_t *mctx;
    950 
    951 	/*
    952 	 * Destroy a timer manager.
    953 	 */
    954 
    955 	REQUIRE(managerp != NULL);
    956 	manager = (isc__timermgr_t *)*managerp;
    957 	REQUIRE(VALID_MANAGER(manager));
    958 
    959 	LOCK(&manager->lock);
    960 
    961 #ifdef USE_SHARED_MANAGER
    962 	manager->refs--;
    963 	if (manager->refs > 0) {
    964 		UNLOCK(&manager->lock);
    965 		*managerp = NULL;
    966 		return;
    967 	}
    968 	timermgr = NULL;
    969 #endif /* USE_SHARED_MANAGER */
    970 
    971 #ifndef USE_TIMER_THREAD
    972 	isc__timermgr_dispatch((isc_timermgr_t *)manager);
    973 #endif
    974 
    975 	REQUIRE(EMPTY(manager->timers));
    976 	manager->done = ISC_TRUE;
    977 
    978 #ifdef USE_TIMER_THREAD
    979 	XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
    980 			      ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
    981 	SIGNAL(&manager->wakeup);
    982 #endif /* USE_TIMER_THREAD */
    983 
    984 	UNLOCK(&manager->lock);
    985 
    986 #ifdef USE_TIMER_THREAD
    987 	/*
    988 	 * Wait for thread to exit.
    989 	 */
    990 	if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
    991 		UNEXPECTED_ERROR(__FILE__, __LINE__,
    992 				 "isc_thread_join() %s",
    993 				 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
    994 						ISC_MSG_FAILED, "failed"));
    995 #endif /* USE_TIMER_THREAD */
    996 
    997 	/*
    998 	 * Clean up.
    999 	 */
   1000 #ifdef USE_TIMER_THREAD
   1001 	(void)isc_condition_destroy(&manager->wakeup);
   1002 #endif /* USE_TIMER_THREAD */
   1003 	DESTROYLOCK(&manager->lock);
   1004 	isc_heap_destroy(&manager->heap);
   1005 	manager->common.impmagic = 0;
   1006 	manager->common.magic = 0;
   1007 	mctx = manager->mctx;
   1008 	isc_mem_put(mctx, manager, sizeof(*manager));
   1009 	isc_mem_detach(&mctx);
   1010 
   1011 	*managerp = NULL;
   1012 
   1013 #ifdef USE_SHARED_MANAGER
   1014 	timermgr = NULL;
   1015 #endif
   1016 }
   1017 
   1018 #ifndef USE_TIMER_THREAD
   1019 isc_result_t
   1020 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
   1021 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
   1022 
   1023 #ifdef USE_SHARED_MANAGER
   1024 	if (manager == NULL)
   1025 		manager = timermgr;
   1026 #endif
   1027 	if (manager == NULL || manager->nscheduled == 0)
   1028 		return (ISC_R_NOTFOUND);
   1029 	*when = manager->due;
   1030 	return (ISC_R_SUCCESS);
   1031 }
   1032 
   1033 void
   1034 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
   1035 	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
   1036 	isc_time_t now;
   1037 
   1038 #ifdef USE_SHARED_MANAGER
   1039 	if (manager == NULL)
   1040 		manager = timermgr;
   1041 #endif
   1042 	if (manager == NULL)
   1043 		return;
   1044 	TIME_NOW(&now);
   1045 	dispatch(manager, &now);
   1046 }
   1047 #endif /* USE_TIMER_THREAD */
   1048 
   1049 isc_result_t
   1050 isc__timer_register(void) {
   1051 	return (isc_timer_register(isc__timermgr_create));
   1052 }
   1053 
   1054 static isc_mutex_t createlock;
   1055 static isc_once_t once = ISC_ONCE_INIT;
   1056 static isc_timermgrcreatefunc_t timermgr_createfunc = NULL;
   1057 
   1058 static void
   1059 initialize(void) {
   1060 	RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS);
   1061 }
   1062 
   1063 isc_result_t
   1064 isc_timer_register(isc_timermgrcreatefunc_t createfunc) {
   1065 	isc_result_t result = ISC_R_SUCCESS;
   1066 
   1067 	RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
   1068 
   1069 	LOCK(&createlock);
   1070 	if (timermgr_createfunc == NULL)
   1071 		timermgr_createfunc = createfunc;
   1072 	else
   1073 		result = ISC_R_EXISTS;
   1074 	UNLOCK(&createlock);
   1075 
   1076 	return (result);
   1077 }
   1078 
   1079 isc_result_t
   1080 isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx,
   1081 			 isc_timermgr_t **managerp)
   1082 {
   1083 	isc_result_t result;
   1084 
   1085 	LOCK(&createlock);
   1086 
   1087 	REQUIRE(timermgr_createfunc != NULL);
   1088 	result = (*timermgr_createfunc)(mctx, managerp);
   1089 
   1090 	UNLOCK(&createlock);
   1091 
   1092 	if (result == ISC_R_SUCCESS)
   1093 		isc_appctx_settimermgr(actx, *managerp);
   1094 
   1095 	return (result);
   1096 }
   1097 
   1098 isc_result_t
   1099 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
   1100 	isc_result_t result;
   1101 
   1102 	if (isc_bind9)
   1103 		return (isc__timermgr_create(mctx, managerp));
   1104 
   1105 	LOCK(&createlock);
   1106 
   1107 	REQUIRE(timermgr_createfunc != NULL);
   1108 	result = (*timermgr_createfunc)(mctx, managerp);
   1109 
   1110 	UNLOCK(&createlock);
   1111 
   1112 	return (result);
   1113 }
   1114 
   1115 void
   1116 isc_timermgr_destroy(isc_timermgr_t **managerp) {
   1117 	REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp));
   1118 
   1119 	if (isc_bind9)
   1120 		isc__timermgr_destroy(managerp);
   1121 	else
   1122 		(*managerp)->methods->destroy(managerp);
   1123 
   1124 	ENSURE(*managerp == NULL);
   1125 }
   1126 
   1127 isc_result_t
   1128 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
   1129 		 const isc_time_t *expires, const isc_interval_t *interval,
   1130 		 isc_task_t *task, isc_taskaction_t action, void *arg,
   1131 		 isc_timer_t **timerp)
   1132 {
   1133 	REQUIRE(ISCAPI_TIMERMGR_VALID(manager));
   1134 
   1135 	if (isc_bind9)
   1136 		return (isc__timer_create(manager, type, expires, interval,
   1137 					  task, action, arg, timerp));
   1138 
   1139 	return (manager->methods->timercreate(manager, type, expires,
   1140 					      interval, task, action, arg,
   1141 					      timerp));
   1142 }
   1143 
   1144 void
   1145 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
   1146 	REQUIRE(ISCAPI_TIMER_VALID(timer));
   1147 	REQUIRE(timerp != NULL && *timerp == NULL);
   1148 
   1149 	if (isc_bind9)
   1150 		isc__timer_attach(timer, timerp);
   1151 	else
   1152 		timer->methods->attach(timer, timerp);
   1153 
   1154 	ENSURE(*timerp == timer);
   1155 }
   1156 
   1157 void
   1158 isc_timer_detach(isc_timer_t **timerp) {
   1159 	REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp));
   1160 
   1161 	if (isc_bind9)
   1162 		isc__timer_detach(timerp);
   1163 	else
   1164 		(*timerp)->methods->detach(timerp);
   1165 
   1166 	ENSURE(*timerp == NULL);
   1167 }
   1168 
   1169 isc_result_t
   1170 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
   1171 		const isc_time_t *expires, const isc_interval_t *interval,
   1172 		isc_boolean_t purge)
   1173 {
   1174 	REQUIRE(ISCAPI_TIMER_VALID(timer));
   1175 
   1176 	if (isc_bind9)
   1177 		return (isc__timer_reset(timer, type, expires,
   1178 					 interval, purge));
   1179 
   1180 	return (timer->methods->reset(timer, type, expires, interval, purge));
   1181 }
   1182 
   1183 isc_result_t
   1184 isc_timer_touch(isc_timer_t *timer) {
   1185 	REQUIRE(ISCAPI_TIMER_VALID(timer));
   1186 
   1187 	if (isc_bind9)
   1188 		return (isc__timer_touch(timer));
   1189 
   1190 	return (timer->methods->touch(timer));
   1191 }
   1192