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