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 
74 typedef struct isc__timer isc__timer_t;
75 typedef struct isc__timermgr isc__timermgr_t;
76 
77 struct 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 
100 struct 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 
131 ISC_TIMERFUNC_SCOPE isc_result_t
132 isc__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);
136 ISC_TIMERFUNC_SCOPE isc_result_t
137 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
138 		 isc_time_t *expires, isc_interval_t *interval,
139 		 isc_boolean_t purge);
140 ISC_TIMERFUNC_SCOPE isc_timertype_t
141 isc__timer_gettype(isc_timer_t *timer);
142 ISC_TIMERFUNC_SCOPE isc_result_t
143 isc__timer_touch(isc_timer_t *timer);
144 ISC_TIMERFUNC_SCOPE void
145 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
146 ISC_TIMERFUNC_SCOPE void
147 isc__timer_detach(isc_timer_t **timerp);
148 ISC_TIMERFUNC_SCOPE isc_result_t
149 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
150 ISC_TIMERFUNC_SCOPE void
151 isc__timermgr_poke(isc_timermgr_t *manager0);
152 ISC_TIMERFUNC_SCOPE void
153 isc__timermgr_destroy(isc_timermgr_t **managerp);
154 
155 static 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 
177 static 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  */
197 static isc__timermgr_t *timermgr = NULL;
198 #endif /* USE_SHARED_MANAGER */
199 
200 static inline isc_result_t
schedule(isc__timer_t * timer,isc_time_t * now,isc_boolean_t signal_ok)201 schedule(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 
334 static inline void
deschedule(isc__timer_t * timer)335 deschedule(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 
366 static void
destroy(isc__timer_t * timer)367 destroy(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 
393 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_create(isc_timermgr_t * manager0,isc_timertype_t type,isc_time_t * expires,isc_interval_t * interval,isc_task_t * task,isc_taskaction_t action,const void * arg,isc_timer_t ** timerp)394 isc__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 
515 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_reset(isc_timer_t * timer0,isc_timertype_t type,isc_time_t * expires,isc_interval_t * interval,isc_boolean_t purge)516 isc__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 
591 ISC_TIMERFUNC_SCOPE isc_timertype_t
isc__timer_gettype(isc_timer_t * timer0)592 isc__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 
605 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timer_touch(isc_timer_t * timer0)606 isc__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 
636 ISC_TIMERFUNC_SCOPE void
isc__timer_attach(isc_timer_t * timer0,isc_timer_t ** timerp)637 isc__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 
654 ISC_TIMERFUNC_SCOPE void
isc__timer_detach(isc_timer_t ** timerp)655 isc__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 
680 static void
dispatch(isc__timermgr_t * manager,isc_time_t * now)681 dispatch(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
800 static isc_threadresult_t
801 #ifdef _WIN32			/* XXXDCL */
802 WINAPI
803 #endif
run(void * uap)804 run(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 
846 static isc_boolean_t
sooner(void * v1,void * v2)847 sooner(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 
860 static void
set_index(void * what,unsigned int index)861 set_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 
870 ISC_TIMERFUNC_SCOPE isc_result_t
isc__timermgr_create(isc_mem_t * mctx,isc_timermgr_t ** managerp)871 isc__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 
951 ISC_TIMERFUNC_SCOPE void
isc__timermgr_poke(isc_timermgr_t * manager0)952 isc__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 
964 ISC_TIMERFUNC_SCOPE void
isc__timermgr_destroy(isc_timermgr_t ** managerp)965 isc__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
1037 isc_result_t
isc__timermgr_nextevent(isc_timermgr_t * manager0,isc_time_t * when)1038 isc__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 
1051 void
isc__timermgr_dispatch(isc_timermgr_t * manager0)1052 isc__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
1068 isc_result_t
isc__timer_register()1069 isc__timer_register() {
1070 	return (isc_timer_register(isc__timermgr_create));
1071 }
1072 #endif
1073