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