timer.c 18.2 KB
Newer Older
Bob Halley's avatar
Bob Halley committed
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
Bob Halley's avatar
Bob Halley committed
10
 */
Bob Halley's avatar
add  
Bob Halley committed
11

12 13

/*! \file */
David Lawrence's avatar
David Lawrence committed
14

15 16
#include <stdbool.h>

17
#include <isc/app.h>
18
#include <isc/condition.h>
Bob Halley's avatar
Bob Halley committed
19
#include <isc/heap.h>
20
#include <isc/log.h>
21
#include <isc/magic.h>
22
#include <isc/mem.h>
23
#include <isc/once.h>
24
#include <isc/platform.h>
25
#include <isc/print.h>
David Lawrence's avatar
David Lawrence committed
26
#include <isc/task.h>
27
#include <isc/thread.h>
28
#include <isc/time.h>
Bob Halley's avatar
add  
Bob Halley committed
29
#include <isc/timer.h>
Michael Graff's avatar
Michael Graff committed
30
#include <isc/util.h>
Bob Halley's avatar
add  
Bob Halley committed
31

Francis Dupont's avatar
Francis Dupont committed
32 33 34 35
#ifdef OPENSSL_LEAKS
#include <openssl/err.h>
#endif

Bob Halley's avatar
Bob Halley committed
36
#ifdef ISC_TIMER_TRACE
37 38 39
#define XTRACE(s)			fprintf(stderr, "%s\n", (s))
#define XTRACEID(s, t)			fprintf(stderr, "%s %p\n", (s), (t))
#define XTRACETIME(s, d)		fprintf(stderr, "%s %u.%09u\n", (s), \
Bob Halley's avatar
Bob Halley committed
40
					       (d).seconds, (d).nanoseconds)
41 42 43
#define XTRACETIME2(s, d, n)		fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
					       (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
#define XTRACETIMER(s, t, d)		fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
Bob Halley's avatar
Bob Halley committed
44 45 46 47 48
					       (d).seconds, (d).nanoseconds)
#else
#define XTRACE(s)
#define XTRACEID(s, t)
#define XTRACETIME(s, d)
49
#define XTRACETIME2(s, d, n)
Bob Halley's avatar
Bob Halley committed
50
#define XTRACETIMER(s, t, d)
Bob Halley's avatar
Bob Halley committed
51
#endif /* ISC_TIMER_TRACE */
Bob Halley's avatar
Bob Halley committed
52

53 54 55
#define TIMER_MAGIC			ISC_MAGIC('T', 'I', 'M', 'R')
#define VALID_TIMER(t)			ISC_MAGIC_VALID(t, TIMER_MAGIC)

56 57 58 59
typedef struct isc__timer isc__timer_t;
typedef struct isc__timermgr isc__timermgr_t;

struct isc__timer {
60
	/*! Not locked. */
61 62
	isc_timer_t			common;
	isc__timermgr_t *		manager;
Bob Halley's avatar
Bob Halley committed
63
	isc_mutex_t			lock;
64
	/*! Locked by timer lock. */
Bob Halley's avatar
add  
Bob Halley committed
65
	unsigned int			references;
Bob Halley's avatar
Bob Halley committed
66
	isc_time_t			idle;
67
	/*! Locked by manager lock. */
Bob Halley's avatar
Bob Halley committed
68
	isc_timertype_t			type;
Bob Halley's avatar
Bob Halley committed
69 70 71
	isc_time_t			expires;
	isc_interval_t			interval;
	isc_task_t *			task;
Bob Halley's avatar
Bob Halley committed
72
	isc_taskaction_t		action;
Bob Halley's avatar
add  
Bob Halley committed
73
	void *				arg;
Bob Halley's avatar
Bob Halley committed
74
	unsigned int			index;
Bob Halley's avatar
Bob Halley committed
75
	isc_time_t			due;
76
	LINK(isc__timer_t)		link;
Bob Halley's avatar
add  
Bob Halley committed
77 78
};

79 80
#define TIMER_MANAGER_MAGIC		ISC_MAGIC('T', 'I', 'M', 'M')
#define VALID_MANAGER(m)		ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
Bob Halley's avatar
add  
Bob Halley committed
81

82
struct isc__timermgr {
Bob Halley's avatar
add  
Bob Halley committed
83
	/* Not locked. */
84
	isc_timermgr_t			common;
Bob Halley's avatar
Bob Halley committed
85
	isc_mem_t *			mctx;
Bob Halley's avatar
Bob Halley committed
86
	isc_mutex_t			lock;
Bob Halley's avatar
add  
Bob Halley committed
87
	/* Locked by manager lock. */
88
	bool			done;
89
	LIST(isc__timer_t)		timers;
Bob Halley's avatar
Bob Halley committed
90
	unsigned int			nscheduled;
Bob Halley's avatar
Bob Halley committed
91
	isc_time_t			due;
Bob Halley's avatar
Bob Halley committed
92 93
	isc_condition_t			wakeup;
	isc_thread_t			thread;
Bob Halley's avatar
Bob Halley committed
94
	isc_heap_t *			heap;
Bob Halley's avatar
add  
Bob Halley committed
95 96
};

97 98
void
isc_timermgr_poke(isc_timermgr_t *manager0);
99

Bob Halley's avatar
Bob Halley committed
100
static inline isc_result_t
101
schedule(isc__timer_t *timer, isc_time_t *now, bool signal_ok) {
Bob Halley's avatar
Bob Halley committed
102
	isc_result_t result;
103
	isc__timermgr_t *manager;
Bob Halley's avatar
Bob Halley committed
104
	isc_time_t due;
Bob Halley's avatar
Bob Halley committed
105
	int cmp;
Bob Halley's avatar
Bob Halley committed
106

107
	/*!
Bob Halley's avatar
Bob Halley committed
108 109 110
	 * Note: the caller must ensure locking.
	 */

111 112
	REQUIRE(timer->type != isc_timertype_inactive);

113 114
	manager = timer->manager;

Bob Halley's avatar
Bob Halley committed
115 116
	/*
	 * Compute the new due time.
Bob Halley's avatar
Bob Halley committed
117
	 */
118
	if (timer->type != isc_timertype_once) {
119 120 121
		result = isc_time_add(now, &timer->interval, &due);
		if (result != ISC_R_SUCCESS)
			return (result);
122 123 124
		if (timer->type == isc_timertype_limited &&
		    isc_time_compare(&timer->expires, &due) < 0)
			due = timer->expires;
125
	} else {
126
		if (isc_time_isepoch(&timer->idle))
Bob Halley's avatar
Bob Halley committed
127
			due = timer->expires;
128
		else if (isc_time_isepoch(&timer->expires))
Bob Halley's avatar
Bob Halley committed
129
			due = timer->idle;
Bob Halley's avatar
Bob Halley committed
130
		else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
Bob Halley's avatar
Bob Halley committed
131 132 133 134
			due = timer->idle;
		else
			due = timer->expires;
	}
135

Bob Halley's avatar
Bob Halley committed
136 137 138
	/*
	 * Schedule the timer.
	 */
139

Bob Halley's avatar
Bob Halley committed
140 141 142 143
	if (timer->index > 0) {
		/*
		 * Already scheduled.
		 */
Bob Halley's avatar
Bob Halley committed
144
		cmp = isc_time_compare(&due, &timer->due);
Bob Halley's avatar
Bob Halley committed
145 146
		timer->due = due;
		switch (cmp) {
Bob Halley's avatar
Bob Halley committed
147
		case -1:
Bob Halley's avatar
Bob Halley committed
148
			isc_heap_increased(manager->heap, timer->index);
Bob Halley's avatar
Bob Halley committed
149 150
			break;
		case 1:
Bob Halley's avatar
Bob Halley committed
151
			isc_heap_decreased(manager->heap, timer->index);
Bob Halley's avatar
Bob Halley committed
152 153 154 155 156 157
			break;
		case 0:
			/* Nothing to do. */
			break;
		}
	} else {
Bob Halley's avatar
Bob Halley committed
158
		timer->due = due;
Bob Halley's avatar
Bob Halley committed
159
		result = isc_heap_insert(manager->heap, timer);
Bob Halley's avatar
Bob Halley committed
160 161 162 163 164 165
		if (result != ISC_R_SUCCESS) {
			INSIST(result == ISC_R_NOMEMORY);
			return (ISC_R_NOMEMORY);
		}
		manager->nscheduled++;
	}
Bob Halley's avatar
Bob Halley committed
166

Ondřej Surý's avatar
Ondřej Surý committed
167
	XTRACETIMER("schedule", timer, due);
Bob Halley's avatar
Bob Halley committed
168 169

	/*
170 171 172 173
	 * If this timer is at the head of the queue, we need to ensure
	 * that we won't miss it if it has a more recent due time than
	 * the current "next" timer.  We do this either by waking up the
	 * run thread, or explicitly setting the value in the manager.
Bob Halley's avatar
Bob Halley committed
174
	 */
175

176
	if (timer->index == 1 && signal_ok) {
Ondřej Surý's avatar
Ondřej Surý committed
177
		XTRACE("signal (schedule)");
178
		SIGNAL(&manager->wakeup);
Bob Halley's avatar
Bob Halley committed
179
	}
Bob Halley's avatar
Bob Halley committed
180 181 182 183

	return (ISC_R_SUCCESS);
}

Bob Halley's avatar
add  
Bob Halley committed
184
static inline void
185
deschedule(isc__timer_t *timer) {
186
	bool need_wakeup = false;
187
	isc__timermgr_t *manager;
Bob Halley's avatar
Bob Halley committed
188

189
	/*
Bob Halley's avatar
add  
Bob Halley committed
190 191 192
	 * The caller must ensure locking.
	 */

Bob Halley's avatar
Bob Halley committed
193 194 195
	manager = timer->manager;
	if (timer->index > 0) {
		if (timer->index == 1)
196
			need_wakeup = true;
Bob Halley's avatar
Bob Halley committed
197
		isc_heap_delete(manager->heap, timer->index);
Bob Halley's avatar
Bob Halley committed
198 199 200
		timer->index = 0;
		INSIST(manager->nscheduled > 0);
		manager->nscheduled--;
Bob Halley's avatar
Bob Halley committed
201
		if (need_wakeup) {
Ondřej Surý's avatar
Ondřej Surý committed
202
			XTRACE("signal (deschedule)");
203
			SIGNAL(&manager->wakeup);
Bob Halley's avatar
Bob Halley committed
204
		}
Bob Halley's avatar
Bob Halley committed
205
	}
Bob Halley's avatar
add  
Bob Halley committed
206 207 208
}

static void
209 210
destroy(isc__timer_t *timer) {
	isc__timermgr_t *manager = timer->manager;
Bob Halley's avatar
add  
Bob Halley committed
211 212

	/*
213
	 * The caller must ensure it is safe to destroy the timer.
Bob Halley's avatar
add  
Bob Halley committed
214 215 216 217
	 */

	LOCK(&manager->lock);

218 219 220 221 222
	(void)isc_task_purgerange(timer->task,
				  timer,
				  ISC_TIMEREVENT_FIRSTEVENT,
				  ISC_TIMEREVENT_LASTEVENT,
				  NULL);
Bob Halley's avatar
add  
Bob Halley committed
223 224 225 226 227
	deschedule(timer);
	UNLINK(manager->timers, timer, link);

	UNLOCK(&manager->lock);

Bob Halley's avatar
Bob Halley committed
228
	isc_task_detach(&timer->task);
229
	isc_mutex_destroy(&timer->lock);
230 231
	timer->common.impmagic = 0;
	timer->common.magic = 0;
Andreas Gustafsson's avatar
Andreas Gustafsson committed
232
	isc_mem_put(manager->mctx, timer, sizeof(*timer));
Bob Halley's avatar
add  
Bob Halley committed
233 234
}

235
isc_result_t
236 237 238 239
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)
Bob Halley's avatar
add  
Bob Halley committed
240
{
241 242
	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
	isc__timer_t *timer;
Bob Halley's avatar
Bob Halley committed
243
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
244
	isc_time_t now;
Bob Halley's avatar
add  
Bob Halley committed
245 246 247

	/*
	 * Create a new 'type' timer managed by 'manager'.  The timers
Bob Halley's avatar
Bob Halley committed
248
	 * parameters are specified by 'expires' and 'interval'.  Events
Bob Halley's avatar
Bob Halley committed
249 250 251
	 * will be posted to 'task' and when dispatched 'action' will be
	 * called with 'arg' as the arg value.  The new timer is returned
	 * in 'timerp'.
Bob Halley's avatar
add  
Bob Halley committed
252 253 254 255
	 */

	REQUIRE(VALID_MANAGER(manager));
	REQUIRE(task != NULL);
Bob Halley's avatar
Bob Halley committed
256
	REQUIRE(action != NULL);
257 258 259 260 261
	if (expires == NULL)
		expires = isc_time_epoch;
	if (interval == NULL)
		interval = isc_interval_zero;
	REQUIRE(type == isc_timertype_inactive ||
262
		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
Bob Halley's avatar
add  
Bob Halley committed
263
	REQUIRE(timerp != NULL && *timerp == NULL);
264 265
	REQUIRE(type != isc_timertype_limited ||
		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
Bob Halley's avatar
add  
Bob Halley committed
266 267 268 269

	/*
	 * Get current time.
	 */
270
	if (type != isc_timertype_inactive) {
271
		TIME_NOW(&now);
272 273 274 275 276 277 278
	} else {
		/*
		 * We don't have to do this, but it keeps the compiler from
		 * complaining about "now" possibly being used without being
		 * set, even though it will never actually happen.
		 */
		isc_time_settoepoch(&now);
Bob Halley's avatar
add  
Bob Halley committed
279 280
	}

281

Andreas Gustafsson's avatar
Andreas Gustafsson committed
282
	timer = isc_mem_get(manager->mctx, sizeof(*timer));
Bob Halley's avatar
add  
Bob Halley committed
283 284 285 286 287
	if (timer == NULL)
		return (ISC_R_NOMEMORY);

	timer->manager = manager;
	timer->references = 1;
288 289 290

	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
		result = isc_time_add(&now, interval, &timer->idle);
291 292
		if (result != ISC_R_SUCCESS) {
			isc_mem_put(manager->mctx, timer, sizeof(*timer));
293
			return (result);
294
		}
295
	} else
296
		isc_time_settoepoch(&timer->idle);
297

Bob Halley's avatar
add  
Bob Halley committed
298
	timer->type = type;
Bob Halley's avatar
Bob Halley committed
299 300
	timer->expires = *expires;
	timer->interval = *interval;
Bob Halley's avatar
add  
Bob Halley committed
301
	timer->task = NULL;
Bob Halley's avatar
Bob Halley committed
302
	isc_task_attach(task, &timer->task);
Bob Halley's avatar
Bob Halley committed
303
	timer->action = action;
David Lawrence's avatar
David Lawrence committed
304 305 306 307 308 309 310 311 312 313 314
	/*
	 * Removing the const attribute from "arg" is the best of two
	 * evils here.  If the timer->arg member is made const, then
	 * it affects a great many recipients of the timer event
	 * which did not pass in an "arg" that was truly const.
	 * Changing isc_timer_create() to not have "arg" prototyped as const,
	 * though, can cause compilers warnings for calls that *do*
	 * have a truly const arg.  The caller will have to carefully
	 * keep track of whether arg started as a true const.
	 */
	DE_CONST(arg, timer->arg);
Bob Halley's avatar
Bob Halley committed
315
	timer->index = 0;
Ondřej Surý's avatar
Ondřej Surý committed
316
	isc_mutex_init(&timer->lock);
317
	ISC_LINK_INIT(timer, link);
318 319
	timer->common.impmagic = TIMER_MAGIC;
	timer->common.magic = ISCAPI_TIMER_MAGIC;
Bob Halley's avatar
add  
Bob Halley committed
320 321 322 323 324 325 326 327

	LOCK(&manager->lock);

	/*
	 * Note we don't have to lock the timer like we normally would because
	 * there are no external references to it yet.
	 */

328
	if (type != isc_timertype_inactive)
329
		result = schedule(timer, &now, true);
330 331
	else
		result = ISC_R_SUCCESS;
332 333
	if (result == ISC_R_SUCCESS) {
		*timerp = (isc_timer_t *)timer;
334
		APPEND(manager->timers, timer, link);
335
	}
336

Bob Halley's avatar
add  
Bob Halley committed
337 338
	UNLOCK(&manager->lock);

339
	if (result != ISC_R_SUCCESS) {
340 341
		timer->common.impmagic = 0;
		timer->common.magic = 0;
342
		isc_mutex_destroy(&timer->lock);
343
		isc_task_detach(&timer->task);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
344
		isc_mem_put(manager->mctx, timer, sizeof(*timer));
345 346 347 348
		return (result);
	}

	return (ISC_R_SUCCESS);
Bob Halley's avatar
add  
Bob Halley committed
349 350
}

351
isc_result_t
352
isc_timer_reset(isc_timer_t *timer0, isc_timertype_t type,
353
		 const isc_time_t *expires, const isc_interval_t *interval,
354
		 bool purge)
Bob Halley's avatar
add  
Bob Halley committed
355
{
356
	isc__timer_t *timer = (isc__timer_t *)timer0;
Bob Halley's avatar
Bob Halley committed
357
	isc_time_t now;
358
	isc__timermgr_t *manager;
Bob Halley's avatar
Bob Halley committed
359
	isc_result_t result;
Bob Halley's avatar
add  
Bob Halley committed
360 361

	/*
362
	 * Change the timer's type, expires, and interval values to the given
363
	 * values.  If 'purge' is true, any pending events from this timer
364
	 * are purged from its task's event queue.
Bob Halley's avatar
add  
Bob Halley committed
365 366 367 368 369
	 */

	REQUIRE(VALID_TIMER(timer));
	manager = timer->manager;
	REQUIRE(VALID_MANAGER(manager));
370

371 372 373 374 375
	if (expires == NULL)
		expires = isc_time_epoch;
	if (interval == NULL)
		interval = isc_interval_zero;
	REQUIRE(type == isc_timertype_inactive ||
376
		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
377 378
	REQUIRE(type != isc_timertype_limited ||
		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
Bob Halley's avatar
add  
Bob Halley committed
379 380 381 382

	/*
	 * Get current time.
	 */
383
	if (type != isc_timertype_inactive) {
384
		TIME_NOW(&now);
385 386 387 388 389 390 391
	} else {
		/*
		 * We don't have to do this, but it keeps the compiler from
		 * complaining about "now" possibly being used without being
		 * set, even though it will never actually happen.
		 */
		isc_time_settoepoch(&now);
Bob Halley's avatar
add  
Bob Halley committed
392 393 394 395 396
	}

	LOCK(&manager->lock);
	LOCK(&timer->lock);

397
	if (purge)
398 399 400 401 402
		(void)isc_task_purgerange(timer->task,
					  timer,
					  ISC_TIMEREVENT_FIRSTEVENT,
					  ISC_TIMEREVENT_LASTEVENT,
					  NULL);
Bob Halley's avatar
add  
Bob Halley committed
403
	timer->type = type;
Bob Halley's avatar
Bob Halley committed
404 405
	timer->expires = *expires;
	timer->interval = *interval;
406 407 408
	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
		result = isc_time_add(&now, interval, &timer->idle);
	} else {
409
		isc_time_settoepoch(&timer->idle);
410
		result = ISC_R_SUCCESS;
411 412 413 414 415 416 417
	}

	if (result == ISC_R_SUCCESS) {
		if (type == isc_timertype_inactive) {
			deschedule(timer);
			result = ISC_R_SUCCESS;
		} else
418
			result = schedule(timer, &now, true);
419
	}
Bob Halley's avatar
add  
Bob Halley committed
420 421 422 423 424 425 426

	UNLOCK(&timer->lock);
	UNLOCK(&manager->lock);

	return (result);
}

427 428
isc_timertype_t
isc_timer_gettype(isc_timer_t *timer0) {
429
	isc__timer_t *timer = (isc__timer_t *)timer0;
430 431 432 433 434 435 436 437 438 439 440
	isc_timertype_t t;

	REQUIRE(VALID_TIMER(timer));

	LOCK(&timer->lock);
	t = timer->type;
	UNLOCK(&timer->lock);

	return (t);
}

441
isc_result_t
442
isc_timer_touch(isc_timer_t *timer0) {
443
	isc__timer_t *timer = (isc__timer_t *)timer0;
Bob Halley's avatar
Bob Halley committed
444
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
445
	isc_time_t now;
Bob Halley's avatar
Bob Halley committed
446

Bob Halley's avatar
add  
Bob Halley committed
447 448 449 450 451 452 453 454
	/*
	 * Set the last-touched time of 'timer' to the current time.
	 */

	REQUIRE(VALID_TIMER(timer));

	LOCK(&timer->lock);

455 456 457 458 459 460 461 462
	/*
	 * We'd like to
	 *
	 *	REQUIRE(timer->type == isc_timertype_once);
	 *
	 * but we cannot without locking the manager lock too, which we
	 * don't want to do.
	 */
Bob Halley's avatar
add  
Bob Halley committed
463

464 465
	TIME_NOW(&now);
	result = isc_time_add(&now, &timer->interval, &timer->idle);
Bob Halley's avatar
add  
Bob Halley committed
466 467 468

	UNLOCK(&timer->lock);

469
	return (result);
Bob Halley's avatar
add  
Bob Halley committed
470 471
}

472
void
473
isc_timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
474 475
	isc__timer_t *timer = (isc__timer_t *)timer0;

Bob Halley's avatar
add  
Bob Halley committed
476 477 478 479 480 481 482 483 484 485
	/*
	 * Attach *timerp to timer.
	 */

	REQUIRE(VALID_TIMER(timer));
	REQUIRE(timerp != NULL && *timerp == NULL);

	LOCK(&timer->lock);
	timer->references++;
	UNLOCK(&timer->lock);
486

487
	*timerp = (isc_timer_t *)timer;
Bob Halley's avatar
add  
Bob Halley committed
488 489
}

490
void
491
isc_timer_detach(isc_timer_t **timerp) {
492
	isc__timer_t *timer;
493
	bool free_timer = false;
Bob Halley's avatar
add  
Bob Halley committed
494 495 496 497 498

	/*
	 * Detach *timerp from its timer.
	 */

Bob Halley's avatar
Bob Halley committed
499
	REQUIRE(timerp != NULL);
500
	timer = (isc__timer_t *)*timerp;
Bob Halley's avatar
add  
Bob Halley committed
501 502 503 504 505 506
	REQUIRE(VALID_TIMER(timer));

	LOCK(&timer->lock);
	REQUIRE(timer->references > 0);
	timer->references--;
	if (timer->references == 0)
507
		free_timer = true;
Bob Halley's avatar
add  
Bob Halley committed
508
	UNLOCK(&timer->lock);
509

Bob Halley's avatar
add  
Bob Halley committed
510 511 512 513 514 515
	if (free_timer)
		destroy(timer);

	*timerp = NULL;
}

Bob Halley's avatar
Bob Halley committed
516
static void
517
dispatch(isc__timermgr_t *manager, isc_time_t *now) {
518
	bool done = false, post_event, need_schedule;
519
	isc_timerevent_t *event;
Bob Halley's avatar
Bob Halley committed
520
	isc_eventtype_t type = 0;
521
	isc__timer_t *timer;
Bob Halley's avatar
Bob Halley committed
522
	isc_result_t result;
523
	bool idle;
Bob Halley's avatar
Bob Halley committed
524

525
	/*!
526 527 528
	 * The caller must be holding the manager lock.
	 */

Bob Halley's avatar
Bob Halley committed
529
	while (manager->nscheduled > 0 && !done) {
Bob Halley's avatar
Bob Halley committed
530
		timer = isc_heap_element(manager->heap, 1);
Evan Hunt's avatar
Evan Hunt committed
531
		INSIST(timer != NULL && timer->type != isc_timertype_inactive);
Bob Halley's avatar
Bob Halley committed
532
		if (isc_time_compare(now, &timer->due) >= 0) {
Bob Halley's avatar
Bob Halley committed
533 534
			if (timer->type == isc_timertype_ticker) {
				type = ISC_TIMEREVENT_TICK;
535 536
				post_event = true;
				need_schedule = true;
537 538 539 540 541
			} else if (timer->type == isc_timertype_limited) {
				int cmp;
				cmp = isc_time_compare(now, &timer->expires);
				if (cmp >= 0) {
					type = ISC_TIMEREVENT_LIFE;
542 543
					post_event = true;
					need_schedule = false;
544 545
				} else {
					type = ISC_TIMEREVENT_TICK;
546 547
					post_event = true;
					need_schedule = true;
548
				}
549
			} else if (!isc_time_isepoch(&timer->expires) &&
Bob Halley's avatar
Bob Halley committed
550
				   isc_time_compare(now,
551
						    &timer->expires) >= 0) {
Bob Halley's avatar
Bob Halley committed
552
				type = ISC_TIMEREVENT_LIFE;
553 554
				post_event = true;
				need_schedule = false;
Bob Halley's avatar
Bob Halley committed
555
			} else {
556
				idle = false;
557 558 559 560 561

				LOCK(&timer->lock);
				if (!isc_time_isepoch(&timer->idle) &&
				    isc_time_compare(now,
						     &timer->idle) >= 0) {
562
					idle = true;
563 564 565 566
				}
				UNLOCK(&timer->lock);
				if (idle) {
					type = ISC_TIMEREVENT_IDLE;
567 568
					post_event = true;
					need_schedule = false;
569 570 571 572 573
				} else {
					/*
					 * Idle timer has been touched;
					 * reschedule.
					 */
Ondřej Surý's avatar
Ondřej Surý committed
574
					XTRACEID("idle reschedule", timer);
575 576
					post_event = false;
					need_schedule = true;
577
				}
Bob Halley's avatar
Bob Halley committed
578 579 580
			}

			if (post_event) {
Ondřej Surý's avatar
Ondřej Surý committed
581
				XTRACEID("posting", timer);
Bob Halley's avatar
note  
Bob Halley committed
582 583 584
				/*
				 * XXX We could preallocate this event.
				 */
585
				event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
Bob Halley's avatar
Bob Halley committed
586 587 588 589
							   timer,
							   type,
							   timer->action,
							   timer->arg,
Andreas Gustafsson's avatar
Andreas Gustafsson committed
590
							   sizeof(*event));
Bob Halley's avatar
update  
Bob Halley committed
591

592 593 594
				if (event != NULL) {
					event->due = timer->due;
					isc_task_send(timer->task,
Mark Andrews's avatar
Mark Andrews committed
595
						      ISC_EVENT_PTR(&event));
596
				} else
Automatic Updater's avatar
Automatic Updater committed
597
					UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
Ondřej Surý's avatar
Ondřej Surý committed
598
							 "couldn't allocate event");
Bob Halley's avatar
Bob Halley committed
599
			}
600

Bob Halley's avatar
Bob Halley committed
601
			timer->index = 0;
Bob Halley's avatar
Bob Halley committed
602
			isc_heap_delete(manager->heap, 1);
Bob Halley's avatar
Bob Halley committed
603 604 605
			manager->nscheduled--;

			if (need_schedule) {
606
				result = schedule(timer, now, false);
Bob Halley's avatar
Bob Halley committed
607
				if (result != ISC_R_SUCCESS)
Mark Andrews's avatar
Mark Andrews committed
608
					UNEXPECTED_ERROR(__FILE__, __LINE__,
Mark Andrews's avatar
Mark Andrews committed
609
							 "%s: %u",
Ondřej Surý's avatar
Ondřej Surý committed
610
							 "couldn't schedule timer",
Bob Halley's avatar
Bob Halley committed
611 612 613
							 result);
			}
		} else {
Bob Halley's avatar
Bob Halley committed
614
			manager->due = timer->due;
615
			done = true;
Bob Halley's avatar
Bob Halley committed
616
		}
617
	}
Bob Halley's avatar
Bob Halley committed
618 619
}

Bob Halley's avatar
update  
Bob Halley committed
620
static isc_threadresult_t
621
#ifdef _WIN32			/* XXXDCL */
622 623
WINAPI
#endif
Bob Halley's avatar
Bob Halley committed
624
run(void *uap) {
625
	isc__timermgr_t *manager = uap;
Bob Halley's avatar
Bob Halley committed
626
	isc_time_t now;
Bob Halley's avatar
Bob Halley committed
627
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
628 629 630

	LOCK(&manager->lock);
	while (!manager->done) {
631
		TIME_NOW(&now);
Bob Halley's avatar
Bob Halley committed
632

Ondřej Surý's avatar
Ondřej Surý committed
633
		XTRACETIME("running", now);
Bob Halley's avatar
Bob Halley committed
634

Bob Halley's avatar
Bob Halley committed
635 636
		dispatch(manager, &now);

Bob Halley's avatar
Bob Halley committed
637
		if (manager->nscheduled > 0) {
Ondřej Surý's avatar
Ondřej Surý committed
638
			XTRACETIME2("waituntil", manager->due, now);
639
			result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
Bob Halley's avatar
Bob Halley committed
640 641
			INSIST(result == ISC_R_SUCCESS ||
			       result == ISC_R_TIMEDOUT);
Bob Halley's avatar
Bob Halley committed
642
		} else {
Ondřej Surý's avatar
Ondřej Surý committed
643
			XTRACETIME("wait", now);
Bob Halley's avatar
Bob Halley committed
644
			WAIT(&manager->wakeup, &manager->lock);
Bob Halley's avatar
Bob Halley committed
645
		}
Ondřej Surý's avatar
Ondřej Surý committed
646
		XTRACE("wakeup");
Bob Halley's avatar
Bob Halley committed
647 648 649
	}
	UNLOCK(&manager->lock);

Francis Dupont's avatar
Francis Dupont committed
650 651 652 653
#ifdef OPENSSL_LEAKS
	ERR_remove_state(0);
#endif

Bob Halley's avatar
update  
Bob Halley committed
654
	return ((isc_threadresult_t)0);
Bob Halley's avatar
Bob Halley committed
655 656
}

657
static bool
658
sooner(void *v1, void *v2) {
659
	isc__timer_t *t1, *t2;
660 661 662 663 664 665

	t1 = v1;
	t2 = v2;
	REQUIRE(VALID_TIMER(t1));
	REQUIRE(VALID_TIMER(t2));

Bob Halley's avatar
Bob Halley committed
666
	if (isc_time_compare(&t1->due, &t2->due) < 0)
667 668
		return (true);
	return (false);
669 670 671 672
}

static void
set_index(void *what, unsigned int index) {
673
	isc__timer_t *timer;
674 675 676 677 678 679 680

	timer = what;
	REQUIRE(VALID_TIMER(timer));

	timer->index = index;
}

681
isc_result_t
682
isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
683
	isc__timermgr_t *manager;
Bob Halley's avatar
Bob Halley committed
684
	isc_result_t result;
Bob Halley's avatar
Bob Halley committed
685 686 687 688 689 690 691

	/*
	 * Create a timer manager.
	 */

	REQUIRE(managerp != NULL && *managerp == NULL);

Andreas Gustafsson's avatar
Andreas Gustafsson committed
692
	manager = isc_mem_get(mctx, sizeof(*manager));
Bob Halley's avatar
Bob Halley committed
693 694
	if (manager == NULL)
		return (ISC_R_NOMEMORY);
695

696 697
	manager->common.impmagic = TIMER_MANAGER_MAGIC;
	manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
698
	manager->mctx = NULL;
699
	manager->done = false;
Bob Halley's avatar
Bob Halley committed
700 701
	INIT_LIST(manager->timers);
	manager->nscheduled = 0;
702
	isc_time_settoepoch(&manager->due);
Bob Halley's avatar
Bob Halley committed
703
	manager->heap = NULL;
Bob Halley's avatar
Bob Halley committed
704
	result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
Bob Halley's avatar
Bob Halley committed
705 706
	if (result != ISC_R_SUCCESS) {
		INSIST(result == ISC_R_NOMEMORY);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
707
		isc_mem_put(mctx, manager, sizeof(*manager));
Bob Halley's avatar
Bob Halley committed
708 709
		return (ISC_R_NOMEMORY);
	}
Ondřej Surý's avatar
Ondřej Surý committed
710
	isc_mutex_init(&manager->lock);
711
	isc_mem_attach(mctx, &manager->mctx);
712
	isc_condition_init(&manager->wakeup);
Bob Halley's avatar
Bob Halley committed
713 714
	if (isc_thread_create(run, manager, &manager->thread) !=
	    ISC_R_SUCCESS) {
715
		isc_mem_detach(&manager->mctx);
Bob Halley's avatar
Bob Halley committed
716
		(void)isc_condition_destroy(&manager->wakeup);
717
		isc_mutex_destroy(&manager->lock);
Bob Halley's avatar
Bob Halley committed
718
		isc_heap_destroy(&manager->heap);
Andreas Gustafsson's avatar
Andreas Gustafsson committed
719
		isc_mem_put(mctx, manager, sizeof(*manager));
Ondřej Surý's avatar
Ondřej Surý committed
720 721
		UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
				 "isc_thread_create() failed");
Bob Halley's avatar
Bob Halley committed
722 723
		return (ISC_R_UNEXPECTED);
	}
724
	isc_thread_setname(manager->thread, "isc-timer");
Bob Halley's avatar
Bob Halley committed
725

726
	*managerp = (isc_timermgr_t *)manager;
Bob Halley's avatar
Bob Halley committed
727 728 729

	return (ISC_R_SUCCESS);
}
Bob Halley's avatar
add  
Bob Halley committed
730

731 732
void
isc_timermgr_poke(isc_timermgr_t *manager0) {
733 734
	isc__timermgr_t *manager = (isc__timermgr_t *)manager0;

735 736
	REQUIRE(VALID_MANAGER(manager));

737 738 739
	SIGNAL(&manager->wakeup);
}

740
void
741
isc_timermgr_destroy(isc_timermgr_t **managerp) {
742
	isc__timermgr_t *manager;
743
	isc_mem_t *mctx;
Bob Halley's avatar
Bob Halley committed
744 745 746 747 748 749

	/*
	 * Destroy a timer manager.
	 */

	REQUIRE(managerp != NULL);
750
	manager = (isc__timermgr_t *)*managerp;
Bob Halley's avatar
Bob Halley committed
751
	REQUIRE(VALID_MANAGER(manager));
Bob Halley's avatar
add  
Bob Halley committed
752

Bob Halley's avatar
Bob Halley committed
753 754 755
	LOCK(&manager->lock);

	REQUIRE(EMPTY(manager->timers));
756
	manager->done = true;
Bob Halley's avatar
Bob Halley committed
757

Ondřej Surý's avatar
Ondřej Surý committed
758
	XTRACE("signal (destroy)");
759
	SIGNAL(&manager->wakeup);
Bob Halley's avatar
Bob Halley committed
760

761 762
	UNLOCK(&manager->lock);

Bob Halley's avatar
Bob Halley committed
763 764 765
	/*
	 * Wait for thread to exit.
	 */
Bob Halley's avatar
update  
Bob Halley committed
766
	if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
Ondřej Surý's avatar
Ondřej Surý committed
767 768
		UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
				 "isc_thread_join() failed");
Bob Halley's avatar
Bob Halley committed
769 770 771 772

	/*
	 * Clean up.
	 */
Bob Halley's avatar
Bob Halley committed
773
	(void)isc_condition_destroy(&manager->wakeup);
774
	isc_mutex_destroy(&manager->lock);
Bob Halley's avatar
Bob Halley committed
775
	isc_heap_destroy(&manager->heap);
776 777
	manager->common.impmagic = 0;
	manager->common.magic = 0;
778
	mctx = manager->mctx;