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