Home | History | Annotate | Line # | Download | only in isc
timer.c revision 1.1.1.4
      1      1.1  christos /*	$NetBSD: timer.c,v 1.1.1.4 2020/05/24 19:36:46 christos Exp $	*/
      2      1.1  christos 
      3      1.1  christos /*
      4      1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5      1.1  christos  *
      6      1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      7      1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8      1.1  christos  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9      1.1  christos  *
     10      1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     11      1.1  christos  * information regarding copyright ownership.
     12      1.1  christos  */
     13      1.1  christos 
     14      1.1  christos /*! \file */
     15      1.1  christos 
     16  1.1.1.2  christos #include <stdbool.h>
     17  1.1.1.2  christos 
     18      1.1  christos #include <isc/app.h>
     19      1.1  christos #include <isc/condition.h>
     20      1.1  christos #include <isc/heap.h>
     21      1.1  christos #include <isc/log.h>
     22      1.1  christos #include <isc/magic.h>
     23      1.1  christos #include <isc/mem.h>
     24      1.1  christos #include <isc/once.h>
     25      1.1  christos #include <isc/platform.h>
     26      1.1  christos #include <isc/print.h>
     27  1.1.1.4  christos #include <isc/refcount.h>
     28      1.1  christos #include <isc/task.h>
     29      1.1  christos #include <isc/thread.h>
     30      1.1  christos #include <isc/time.h>
     31      1.1  christos #include <isc/timer.h>
     32      1.1  christos #include <isc/util.h>
     33      1.1  christos 
     34      1.1  christos #ifdef OPENSSL_LEAKS
     35      1.1  christos #include <openssl/err.h>
     36  1.1.1.4  christos #endif /* ifdef OPENSSL_LEAKS */
     37      1.1  christos 
     38      1.1  christos #ifdef ISC_TIMER_TRACE
     39  1.1.1.4  christos #define XTRACE(s)      fprintf(stderr, "%s\n", (s))
     40  1.1.1.4  christos #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
     41  1.1.1.4  christos #define XTRACETIME(s, d) \
     42  1.1.1.4  christos 	fprintf(stderr, "%s %u.%09u\n", (s), (d).seconds, (d).nanoseconds)
     43  1.1.1.4  christos #define XTRACETIME2(s, d, n)                                      \
     44  1.1.1.4  christos 	fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), (d).seconds, \
     45  1.1.1.4  christos 		(d).nanoseconds, (n).seconds, (n).nanoseconds)
     46  1.1.1.4  christos #define XTRACETIMER(s, t, d)                                      \
     47  1.1.1.4  christos 	fprintf(stderr, "%s %p %u.%09u\n", (s), (t), (d).seconds, \
     48  1.1.1.4  christos 		(d).nanoseconds)
     49  1.1.1.4  christos #else /* ifdef ISC_TIMER_TRACE */
     50      1.1  christos #define XTRACE(s)
     51      1.1  christos #define XTRACEID(s, t)
     52      1.1  christos #define XTRACETIME(s, d)
     53      1.1  christos #define XTRACETIME2(s, d, n)
     54      1.1  christos #define XTRACETIMER(s, t, d)
     55      1.1  christos #endif /* ISC_TIMER_TRACE */
     56      1.1  christos 
     57  1.1.1.4  christos #define TIMER_MAGIC    ISC_MAGIC('T', 'I', 'M', 'R')
     58  1.1.1.4  christos #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
     59      1.1  christos 
     60      1.1  christos typedef struct isc__timer isc__timer_t;
     61      1.1  christos typedef struct isc__timermgr isc__timermgr_t;
     62      1.1  christos 
     63      1.1  christos struct isc__timer {
     64      1.1  christos 	/*! Not locked. */
     65  1.1.1.4  christos 	isc_timer_t common;
     66  1.1.1.4  christos 	isc__timermgr_t *manager;
     67  1.1.1.4  christos 	isc_mutex_t lock;
     68  1.1.1.4  christos 	isc_refcount_t references;
     69      1.1  christos 	/*! Locked by timer lock. */
     70  1.1.1.4  christos 	isc_time_t idle;
     71      1.1  christos 	/*! Locked by manager lock. */
     72  1.1.1.4  christos 	isc_timertype_t type;
     73  1.1.1.4  christos 	isc_time_t expires;
     74  1.1.1.4  christos 	isc_interval_t interval;
     75  1.1.1.4  christos 	isc_task_t *task;
     76  1.1.1.4  christos 	isc_taskaction_t action;
     77  1.1.1.4  christos 	void *arg;
     78  1.1.1.4  christos 	unsigned int index;
     79  1.1.1.4  christos 	isc_time_t due;
     80  1.1.1.4  christos 	LINK(isc__timer_t) link;
     81      1.1  christos };
     82      1.1  christos 
     83  1.1.1.4  christos #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
     84  1.1.1.4  christos #define VALID_MANAGER(m)    ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
     85      1.1  christos 
     86      1.1  christos struct isc__timermgr {
     87      1.1  christos 	/* Not locked. */
     88  1.1.1.4  christos 	isc_timermgr_t common;
     89  1.1.1.4  christos 	isc_mem_t *mctx;
     90  1.1.1.4  christos 	isc_mutex_t lock;
     91      1.1  christos 	/* Locked by manager lock. */
     92  1.1.1.4  christos 	bool done;
     93  1.1.1.4  christos 	LIST(isc__timer_t) timers;
     94  1.1.1.4  christos 	unsigned int nscheduled;
     95  1.1.1.4  christos 	isc_time_t due;
     96  1.1.1.4  christos 	isc_condition_t wakeup;
     97  1.1.1.4  christos 	isc_thread_t thread;
     98  1.1.1.4  christos 	isc_heap_t *heap;
     99      1.1  christos };
    100      1.1  christos 
    101      1.1  christos void
    102      1.1  christos isc_timermgr_poke(isc_timermgr_t *manager0);
    103      1.1  christos 
    104      1.1  christos static inline isc_result_t
    105  1.1.1.2  christos schedule(isc__timer_t *timer, isc_time_t *now, bool signal_ok) {
    106      1.1  christos 	isc_result_t result;
    107      1.1  christos 	isc__timermgr_t *manager;
    108      1.1  christos 	isc_time_t due;
    109      1.1  christos 	int cmp;
    110      1.1  christos 
    111      1.1  christos 	/*!
    112      1.1  christos 	 * Note: the caller must ensure locking.
    113      1.1  christos 	 */
    114      1.1  christos 
    115      1.1  christos 	REQUIRE(timer->type != isc_timertype_inactive);
    116      1.1  christos 
    117      1.1  christos 	manager = timer->manager;
    118      1.1  christos 
    119      1.1  christos 	/*
    120      1.1  christos 	 * Compute the new due time.
    121      1.1  christos 	 */
    122      1.1  christos 	if (timer->type != isc_timertype_once) {
    123      1.1  christos 		result = isc_time_add(now, &timer->interval, &due);
    124  1.1.1.4  christos 		if (result != ISC_R_SUCCESS) {
    125      1.1  christos 			return (result);
    126  1.1.1.4  christos 		}
    127      1.1  christos 		if (timer->type == isc_timertype_limited &&
    128      1.1  christos 		    isc_time_compare(&timer->expires, &due) < 0)
    129  1.1.1.4  christos 		{
    130      1.1  christos 			due = timer->expires;
    131  1.1.1.4  christos 		}
    132      1.1  christos 	} else {
    133  1.1.1.4  christos 		if (isc_time_isepoch(&timer->idle)) {
    134      1.1  christos 			due = timer->expires;
    135  1.1.1.4  christos 		} else if (isc_time_isepoch(&timer->expires)) {
    136      1.1  christos 			due = timer->idle;
    137  1.1.1.4  christos 		} else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
    138  1.1.1.4  christos 		{
    139      1.1  christos 			due = timer->idle;
    140  1.1.1.4  christos 		} else {
    141      1.1  christos 			due = timer->expires;
    142  1.1.1.4  christos 		}
    143      1.1  christos 	}
    144      1.1  christos 
    145      1.1  christos 	/*
    146      1.1  christos 	 * Schedule the timer.
    147      1.1  christos 	 */
    148      1.1  christos 
    149      1.1  christos 	if (timer->index > 0) {
    150      1.1  christos 		/*
    151      1.1  christos 		 * Already scheduled.
    152      1.1  christos 		 */
    153      1.1  christos 		cmp = isc_time_compare(&due, &timer->due);
    154      1.1  christos 		timer->due = due;
    155      1.1  christos 		switch (cmp) {
    156      1.1  christos 		case -1:
    157      1.1  christos 			isc_heap_increased(manager->heap, timer->index);
    158      1.1  christos 			break;
    159      1.1  christos 		case 1:
    160      1.1  christos 			isc_heap_decreased(manager->heap, timer->index);
    161      1.1  christos 			break;
    162      1.1  christos 		case 0:
    163      1.1  christos 			/* Nothing to do. */
    164      1.1  christos 			break;
    165      1.1  christos 		}
    166      1.1  christos 	} else {
    167      1.1  christos 		timer->due = due;
    168      1.1  christos 		result = isc_heap_insert(manager->heap, timer);
    169      1.1  christos 		if (result != ISC_R_SUCCESS) {
    170      1.1  christos 			INSIST(result == ISC_R_NOMEMORY);
    171      1.1  christos 			return (ISC_R_NOMEMORY);
    172      1.1  christos 		}
    173      1.1  christos 		manager->nscheduled++;
    174      1.1  christos 	}
    175      1.1  christos 
    176  1.1.1.3  christos 	XTRACETIMER("schedule", timer, due);
    177      1.1  christos 
    178      1.1  christos 	/*
    179      1.1  christos 	 * If this timer is at the head of the queue, we need to ensure
    180      1.1  christos 	 * that we won't miss it if it has a more recent due time than
    181      1.1  christos 	 * the current "next" timer.  We do this either by waking up the
    182      1.1  christos 	 * run thread, or explicitly setting the value in the manager.
    183      1.1  christos 	 */
    184      1.1  christos 
    185      1.1  christos 	if (timer->index == 1 && signal_ok) {
    186  1.1.1.3  christos 		XTRACE("signal (schedule)");
    187      1.1  christos 		SIGNAL(&manager->wakeup);
    188      1.1  christos 	}
    189      1.1  christos 
    190      1.1  christos 	return (ISC_R_SUCCESS);
    191      1.1  christos }
    192      1.1  christos 
    193      1.1  christos static inline void
    194      1.1  christos deschedule(isc__timer_t *timer) {
    195  1.1.1.2  christos 	bool need_wakeup = false;
    196      1.1  christos 	isc__timermgr_t *manager;
    197      1.1  christos 
    198      1.1  christos 	/*
    199      1.1  christos 	 * The caller must ensure locking.
    200      1.1  christos 	 */
    201      1.1  christos 
    202      1.1  christos 	manager = timer->manager;
    203      1.1  christos 	if (timer->index > 0) {
    204  1.1.1.4  christos 		if (timer->index == 1) {
    205  1.1.1.2  christos 			need_wakeup = true;
    206  1.1.1.4  christos 		}
    207      1.1  christos 		isc_heap_delete(manager->heap, timer->index);
    208      1.1  christos 		timer->index = 0;
    209      1.1  christos 		INSIST(manager->nscheduled > 0);
    210      1.1  christos 		manager->nscheduled--;
    211      1.1  christos 		if (need_wakeup) {
    212  1.1.1.3  christos 			XTRACE("signal (deschedule)");
    213      1.1  christos 			SIGNAL(&manager->wakeup);
    214      1.1  christos 		}
    215      1.1  christos 	}
    216      1.1  christos }
    217      1.1  christos 
    218      1.1  christos static void
    219      1.1  christos destroy(isc__timer_t *timer) {
    220      1.1  christos 	isc__timermgr_t *manager = timer->manager;
    221      1.1  christos 
    222      1.1  christos 	/*
    223      1.1  christos 	 * The caller must ensure it is safe to destroy the timer.
    224      1.1  christos 	 */
    225      1.1  christos 
    226      1.1  christos 	LOCK(&manager->lock);
    227      1.1  christos 
    228  1.1.1.4  christos 	(void)isc_task_purgerange(timer->task, timer, ISC_TIMEREVENT_FIRSTEVENT,
    229  1.1.1.4  christos 				  ISC_TIMEREVENT_LASTEVENT, NULL);
    230      1.1  christos 	deschedule(timer);
    231      1.1  christos 	UNLINK(manager->timers, timer, link);
    232      1.1  christos 
    233      1.1  christos 	UNLOCK(&manager->lock);
    234      1.1  christos 
    235      1.1  christos 	isc_task_detach(&timer->task);
    236  1.1.1.2  christos 	isc_mutex_destroy(&timer->lock);
    237      1.1  christos 	timer->common.impmagic = 0;
    238      1.1  christos 	timer->common.magic = 0;
    239      1.1  christos 	isc_mem_put(manager->mctx, timer, sizeof(*timer));
    240      1.1  christos }
    241      1.1  christos 
    242      1.1  christos isc_result_t
    243  1.1.1.2  christos isc_timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
    244  1.1.1.2  christos 		 const isc_time_t *expires, const isc_interval_t *interval,
    245  1.1.1.2  christos 		 isc_task_t *task, isc_taskaction_t action, void *arg,
    246  1.1.1.4  christos 		 isc_timer_t **timerp) {
    247  1.1.1.4  christos 	REQUIRE(VALID_MANAGER(manager0));
    248  1.1.1.4  christos 	REQUIRE(task != NULL);
    249  1.1.1.4  christos 	REQUIRE(action != NULL);
    250  1.1.1.4  christos 
    251  1.1.1.4  christos 	isc__timermgr_t *manager;
    252      1.1  christos 	isc__timer_t *timer;
    253      1.1  christos 	isc_result_t result;
    254      1.1  christos 	isc_time_t now;
    255      1.1  christos 
    256      1.1  christos 	/*
    257      1.1  christos 	 * Create a new 'type' timer managed by 'manager'.  The timers
    258      1.1  christos 	 * parameters are specified by 'expires' and 'interval'.  Events
    259      1.1  christos 	 * will be posted to 'task' and when dispatched 'action' will be
    260      1.1  christos 	 * called with 'arg' as the arg value.  The new timer is returned
    261      1.1  christos 	 * in 'timerp'.
    262      1.1  christos 	 */
    263  1.1.1.4  christos 	manager = (isc__timermgr_t *)manager0;
    264  1.1.1.4  christos 	if (expires == NULL) {
    265      1.1  christos 		expires = isc_time_epoch;
    266  1.1.1.4  christos 	}
    267  1.1.1.4  christos 	if (interval == NULL) {
    268      1.1  christos 		interval = isc_interval_zero;
    269  1.1.1.4  christos 	}
    270      1.1  christos 	REQUIRE(type == isc_timertype_inactive ||
    271      1.1  christos 		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
    272      1.1  christos 	REQUIRE(timerp != NULL && *timerp == NULL);
    273      1.1  christos 	REQUIRE(type != isc_timertype_limited ||
    274      1.1  christos 		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
    275      1.1  christos 
    276      1.1  christos 	/*
    277      1.1  christos 	 * Get current time.
    278      1.1  christos 	 */
    279      1.1  christos 	if (type != isc_timertype_inactive) {
    280      1.1  christos 		TIME_NOW(&now);
    281      1.1  christos 	} else {
    282      1.1  christos 		/*
    283      1.1  christos 		 * We don't have to do this, but it keeps the compiler from
    284      1.1  christos 		 * complaining about "now" possibly being used without being
    285      1.1  christos 		 * set, even though it will never actually happen.
    286      1.1  christos 		 */
    287      1.1  christos 		isc_time_settoepoch(&now);
    288      1.1  christos 	}
    289      1.1  christos 
    290      1.1  christos 	timer = isc_mem_get(manager->mctx, sizeof(*timer));
    291      1.1  christos 
    292      1.1  christos 	timer->manager = manager;
    293  1.1.1.4  christos 	isc_refcount_init(&timer->references, 1);
    294      1.1  christos 
    295      1.1  christos 	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
    296      1.1  christos 		result = isc_time_add(&now, interval, &timer->idle);
    297      1.1  christos 		if (result != ISC_R_SUCCESS) {
    298      1.1  christos 			isc_mem_put(manager->mctx, timer, sizeof(*timer));
    299      1.1  christos 			return (result);
    300      1.1  christos 		}
    301  1.1.1.4  christos 	} else {
    302      1.1  christos 		isc_time_settoepoch(&timer->idle);
    303  1.1.1.4  christos 	}
    304      1.1  christos 
    305      1.1  christos 	timer->type = type;
    306      1.1  christos 	timer->expires = *expires;
    307      1.1  christos 	timer->interval = *interval;
    308      1.1  christos 	timer->task = NULL;
    309      1.1  christos 	isc_task_attach(task, &timer->task);
    310      1.1  christos 	timer->action = action;
    311      1.1  christos 	/*
    312      1.1  christos 	 * Removing the const attribute from "arg" is the best of two
    313      1.1  christos 	 * evils here.  If the timer->arg member is made const, then
    314      1.1  christos 	 * it affects a great many recipients of the timer event
    315      1.1  christos 	 * which did not pass in an "arg" that was truly const.
    316      1.1  christos 	 * Changing isc_timer_create() to not have "arg" prototyped as const,
    317      1.1  christos 	 * though, can cause compilers warnings for calls that *do*
    318      1.1  christos 	 * have a truly const arg.  The caller will have to carefully
    319      1.1  christos 	 * keep track of whether arg started as a true const.
    320      1.1  christos 	 */
    321      1.1  christos 	DE_CONST(arg, timer->arg);
    322      1.1  christos 	timer->index = 0;
    323  1.1.1.2  christos 	isc_mutex_init(&timer->lock);
    324      1.1  christos 	ISC_LINK_INIT(timer, link);
    325      1.1  christos 	timer->common.impmagic = TIMER_MAGIC;
    326      1.1  christos 	timer->common.magic = ISCAPI_TIMER_MAGIC;
    327      1.1  christos 
    328      1.1  christos 	LOCK(&manager->lock);
    329      1.1  christos 
    330      1.1  christos 	/*
    331      1.1  christos 	 * Note we don't have to lock the timer like we normally would because
    332      1.1  christos 	 * there are no external references to it yet.
    333      1.1  christos 	 */
    334      1.1  christos 
    335  1.1.1.4  christos 	if (type != isc_timertype_inactive) {
    336  1.1.1.2  christos 		result = schedule(timer, &now, true);
    337  1.1.1.4  christos 	} else {
    338      1.1  christos 		result = ISC_R_SUCCESS;
    339  1.1.1.4  christos 	}
    340  1.1.1.2  christos 	if (result == ISC_R_SUCCESS) {
    341  1.1.1.2  christos 		*timerp = (isc_timer_t *)timer;
    342      1.1  christos 		APPEND(manager->timers, timer, link);
    343  1.1.1.2  christos 	}
    344      1.1  christos 
    345      1.1  christos 	UNLOCK(&manager->lock);
    346      1.1  christos 
    347      1.1  christos 	if (result != ISC_R_SUCCESS) {
    348      1.1  christos 		timer->common.impmagic = 0;
    349      1.1  christos 		timer->common.magic = 0;
    350  1.1.1.2  christos 		isc_mutex_destroy(&timer->lock);
    351      1.1  christos 		isc_task_detach(&timer->task);
    352      1.1  christos 		isc_mem_put(manager->mctx, timer, sizeof(*timer));
    353      1.1  christos 		return (result);
    354      1.1  christos 	}
    355      1.1  christos 
    356      1.1  christos 	return (ISC_R_SUCCESS);
    357      1.1  christos }
    358      1.1  christos 
    359      1.1  christos isc_result_t
    360  1.1.1.2  christos isc_timer_reset(isc_timer_t *timer0, isc_timertype_t type,
    361  1.1.1.4  christos 		const isc_time_t *expires, const isc_interval_t *interval,
    362  1.1.1.4  christos 		bool purge) {
    363  1.1.1.4  christos 	isc__timer_t *timer;
    364      1.1  christos 	isc_time_t now;
    365      1.1  christos 	isc__timermgr_t *manager;
    366      1.1  christos 	isc_result_t result;
    367      1.1  christos 
    368      1.1  christos 	/*
    369      1.1  christos 	 * Change the timer's type, expires, and interval values to the given
    370  1.1.1.2  christos 	 * values.  If 'purge' is true, any pending events from this timer
    371      1.1  christos 	 * are purged from its task's event queue.
    372      1.1  christos 	 */
    373      1.1  christos 
    374  1.1.1.4  christos 	REQUIRE(VALID_TIMER(timer0));
    375  1.1.1.4  christos 	timer = (isc__timer_t *)timer0;
    376      1.1  christos 	manager = timer->manager;
    377      1.1  christos 	REQUIRE(VALID_MANAGER(manager));
    378      1.1  christos 
    379  1.1.1.4  christos 	if (expires == NULL) {
    380      1.1  christos 		expires = isc_time_epoch;
    381  1.1.1.4  christos 	}
    382  1.1.1.4  christos 	if (interval == NULL) {
    383      1.1  christos 		interval = isc_interval_zero;
    384  1.1.1.4  christos 	}
    385      1.1  christos 	REQUIRE(type == isc_timertype_inactive ||
    386      1.1  christos 		!(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
    387      1.1  christos 	REQUIRE(type != isc_timertype_limited ||
    388      1.1  christos 		!(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
    389      1.1  christos 
    390      1.1  christos 	/*
    391      1.1  christos 	 * Get current time.
    392      1.1  christos 	 */
    393      1.1  christos 	if (type != isc_timertype_inactive) {
    394      1.1  christos 		TIME_NOW(&now);
    395      1.1  christos 	} else {
    396      1.1  christos 		/*
    397      1.1  christos 		 * We don't have to do this, but it keeps the compiler from
    398      1.1  christos 		 * complaining about "now" possibly being used without being
    399      1.1  christos 		 * set, even though it will never actually happen.
    400      1.1  christos 		 */
    401      1.1  christos 		isc_time_settoepoch(&now);
    402      1.1  christos 	}
    403      1.1  christos 
    404      1.1  christos 	LOCK(&manager->lock);
    405      1.1  christos 	LOCK(&timer->lock);
    406      1.1  christos 
    407  1.1.1.4  christos 	if (purge) {
    408  1.1.1.4  christos 		(void)isc_task_purgerange(timer->task, timer,
    409      1.1  christos 					  ISC_TIMEREVENT_FIRSTEVENT,
    410  1.1.1.4  christos 					  ISC_TIMEREVENT_LASTEVENT, NULL);
    411  1.1.1.4  christos 	}
    412      1.1  christos 	timer->type = type;
    413      1.1  christos 	timer->expires = *expires;
    414      1.1  christos 	timer->interval = *interval;
    415      1.1  christos 	if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
    416      1.1  christos 		result = isc_time_add(&now, interval, &timer->idle);
    417      1.1  christos 	} else {
    418      1.1  christos 		isc_time_settoepoch(&timer->idle);
    419      1.1  christos 		result = ISC_R_SUCCESS;
    420      1.1  christos 	}
    421      1.1  christos 
    422      1.1  christos 	if (result == ISC_R_SUCCESS) {
    423      1.1  christos 		if (type == isc_timertype_inactive) {
    424      1.1  christos 			deschedule(timer);
    425      1.1  christos 			result = ISC_R_SUCCESS;
    426  1.1.1.4  christos 		} else {
    427  1.1.1.2  christos 			result = schedule(timer, &now, true);
    428  1.1.1.4  christos 		}
    429      1.1  christos 	}
    430      1.1  christos 
    431      1.1  christos 	UNLOCK(&timer->lock);
    432      1.1  christos 	UNLOCK(&manager->lock);
    433      1.1  christos 
    434      1.1  christos 	return (result);
    435      1.1  christos }
    436      1.1  christos 
    437      1.1  christos isc_timertype_t
    438      1.1  christos isc_timer_gettype(isc_timer_t *timer0) {
    439  1.1.1.4  christos 	isc__timer_t *timer;
    440      1.1  christos 	isc_timertype_t t;
    441      1.1  christos 
    442  1.1.1.4  christos 	REQUIRE(VALID_TIMER(timer0));
    443  1.1.1.4  christos 	timer = (isc__timer_t *)timer0;
    444      1.1  christos 
    445      1.1  christos 	LOCK(&timer->lock);
    446      1.1  christos 	t = timer->type;
    447      1.1  christos 	UNLOCK(&timer->lock);
    448      1.1  christos 
    449      1.1  christos 	return (t);
    450      1.1  christos }
    451      1.1  christos 
    452      1.1  christos isc_result_t
    453  1.1.1.2  christos isc_timer_touch(isc_timer_t *timer0) {
    454  1.1.1.4  christos 	isc__timer_t *timer;
    455      1.1  christos 	isc_result_t result;
    456      1.1  christos 	isc_time_t now;
    457      1.1  christos 
    458      1.1  christos 	/*
    459      1.1  christos 	 * Set the last-touched time of 'timer' to the current time.
    460      1.1  christos 	 */
    461      1.1  christos 
    462  1.1.1.4  christos 	REQUIRE(VALID_TIMER(timer0));
    463  1.1.1.4  christos 	timer = (isc__timer_t *)timer0;
    464      1.1  christos 
    465      1.1  christos 	LOCK(&timer->lock);
    466      1.1  christos 
    467      1.1  christos 	/*
    468      1.1  christos 	 * We'd like to
    469      1.1  christos 	 *
    470      1.1  christos 	 *	REQUIRE(timer->type == isc_timertype_once);
    471      1.1  christos 	 *
    472      1.1  christos 	 * but we cannot without locking the manager lock too, which we
    473      1.1  christos 	 * don't want to do.
    474      1.1  christos 	 */
    475      1.1  christos 
    476      1.1  christos 	TIME_NOW(&now);
    477      1.1  christos 	result = isc_time_add(&now, &timer->interval, &timer->idle);
    478      1.1  christos 
    479      1.1  christos 	UNLOCK(&timer->lock);
    480      1.1  christos 
    481      1.1  christos 	return (result);
    482      1.1  christos }
    483      1.1  christos 
    484      1.1  christos void
    485  1.1.1.2  christos isc_timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
    486  1.1.1.4  christos 	isc__timer_t *timer;
    487      1.1  christos 
    488      1.1  christos 	/*
    489      1.1  christos 	 * Attach *timerp to timer.
    490      1.1  christos 	 */
    491      1.1  christos 
    492  1.1.1.4  christos 	REQUIRE(VALID_TIMER(timer0));
    493  1.1.1.4  christos 	timer = (isc__timer_t *)timer0;
    494      1.1  christos 	REQUIRE(timerp != NULL && *timerp == NULL);
    495  1.1.1.4  christos 	isc_refcount_increment(&timer->references);
    496      1.1  christos 
    497      1.1  christos 	*timerp = (isc_timer_t *)timer;
    498      1.1  christos }
    499      1.1  christos 
    500      1.1  christos void
    501  1.1.1.2  christos isc_timer_detach(isc_timer_t **timerp) {
    502      1.1  christos 	isc__timer_t *timer;
    503      1.1  christos 
    504      1.1  christos 	/*
    505      1.1  christos 	 * Detach *timerp from its timer.
    506      1.1  christos 	 */
    507      1.1  christos 
    508      1.1  christos 	REQUIRE(timerp != NULL);
    509      1.1  christos 	timer = (isc__timer_t *)*timerp;
    510      1.1  christos 	REQUIRE(VALID_TIMER(timer));
    511      1.1  christos 
    512  1.1.1.4  christos 	if (isc_refcount_decrement(&timer->references) == 1) {
    513      1.1  christos 		destroy(timer);
    514  1.1.1.4  christos 	}
    515      1.1  christos 
    516      1.1  christos 	*timerp = NULL;
    517      1.1  christos }
    518      1.1  christos 
    519      1.1  christos static void
    520      1.1  christos dispatch(isc__timermgr_t *manager, isc_time_t *now) {
    521  1.1.1.2  christos 	bool done = false, post_event, need_schedule;
    522      1.1  christos 	isc_timerevent_t *event;
    523      1.1  christos 	isc_eventtype_t type = 0;
    524      1.1  christos 	isc__timer_t *timer;
    525      1.1  christos 	isc_result_t result;
    526  1.1.1.2  christos 	bool idle;
    527      1.1  christos 
    528      1.1  christos 	/*!
    529      1.1  christos 	 * The caller must be holding the manager lock.
    530      1.1  christos 	 */
    531      1.1  christos 
    532      1.1  christos 	while (manager->nscheduled > 0 && !done) {
    533      1.1  christos 		timer = isc_heap_element(manager->heap, 1);
    534      1.1  christos 		INSIST(timer != NULL && timer->type != isc_timertype_inactive);
    535      1.1  christos 		if (isc_time_compare(now, &timer->due) >= 0) {
    536      1.1  christos 			if (timer->type == isc_timertype_ticker) {
    537      1.1  christos 				type = ISC_TIMEREVENT_TICK;
    538  1.1.1.2  christos 				post_event = true;
    539  1.1.1.2  christos 				need_schedule = true;
    540      1.1  christos 			} else if (timer->type == isc_timertype_limited) {
    541      1.1  christos 				int cmp;
    542      1.1  christos 				cmp = isc_time_compare(now, &timer->expires);
    543      1.1  christos 				if (cmp >= 0) {
    544      1.1  christos 					type = ISC_TIMEREVENT_LIFE;
    545  1.1.1.2  christos 					post_event = true;
    546  1.1.1.2  christos 					need_schedule = false;
    547      1.1  christos 				} else {
    548      1.1  christos 					type = ISC_TIMEREVENT_TICK;
    549  1.1.1.2  christos 					post_event = true;
    550  1.1.1.2  christos 					need_schedule = true;
    551      1.1  christos 				}
    552      1.1  christos 			} else if (!isc_time_isepoch(&timer->expires) &&
    553  1.1.1.4  christos 				   isc_time_compare(now, &timer->expires) >= 0)
    554  1.1.1.4  christos 			{
    555      1.1  christos 				type = ISC_TIMEREVENT_LIFE;
    556  1.1.1.2  christos 				post_event = true;
    557  1.1.1.2  christos 				need_schedule = false;
    558      1.1  christos 			} else {
    559  1.1.1.2  christos 				idle = false;
    560      1.1  christos 
    561      1.1  christos 				LOCK(&timer->lock);
    562      1.1  christos 				if (!isc_time_isepoch(&timer->idle) &&
    563  1.1.1.4  christos 				    isc_time_compare(now, &timer->idle) >= 0) {
    564  1.1.1.2  christos 					idle = true;
    565      1.1  christos 				}
    566      1.1  christos 				UNLOCK(&timer->lock);
    567      1.1  christos 				if (idle) {
    568      1.1  christos 					type = ISC_TIMEREVENT_IDLE;
    569  1.1.1.2  christos 					post_event = true;
    570  1.1.1.2  christos 					need_schedule = false;
    571      1.1  christos 				} else {
    572      1.1  christos 					/*
    573      1.1  christos 					 * Idle timer has been touched;
    574      1.1  christos 					 * reschedule.
    575      1.1  christos 					 */
    576  1.1.1.3  christos 					XTRACEID("idle reschedule", timer);
    577  1.1.1.2  christos 					post_event = false;
    578  1.1.1.2  christos 					need_schedule = true;
    579      1.1  christos 				}
    580      1.1  christos 			}
    581      1.1  christos 
    582      1.1  christos 			if (post_event) {
    583  1.1.1.3  christos 				XTRACEID("posting", timer);
    584      1.1  christos 				/*
    585      1.1  christos 				 * XXX We could preallocate this event.
    586      1.1  christos 				 */
    587  1.1.1.4  christos 				event = (isc_timerevent_t *)isc_event_allocate(
    588  1.1.1.4  christos 					manager->mctx, timer, type,
    589  1.1.1.4  christos 					timer->action, timer->arg,
    590  1.1.1.4  christos 					sizeof(*event));
    591      1.1  christos 
    592      1.1  christos 				if (event != NULL) {
    593      1.1  christos 					event->due = timer->due;
    594      1.1  christos 					isc_task_send(timer->task,
    595      1.1  christos 						      ISC_EVENT_PTR(&event));
    596  1.1.1.4  christos 				} else {
    597  1.1.1.4  christos 					UNEXPECTED_ERROR(__FILE__, __LINE__,
    598  1.1.1.4  christos 							 "%s",
    599  1.1.1.4  christos 							 "couldn't allocate "
    600  1.1.1.4  christos 							 "event");
    601  1.1.1.4  christos 				}
    602      1.1  christos 			}
    603      1.1  christos 
    604      1.1  christos 			timer->index = 0;
    605      1.1  christos 			isc_heap_delete(manager->heap, 1);
    606      1.1  christos 			manager->nscheduled--;
    607      1.1  christos 
    608      1.1  christos 			if (need_schedule) {
    609  1.1.1.2  christos 				result = schedule(timer, now, false);
    610  1.1.1.4  christos 				if (result != ISC_R_SUCCESS) {
    611      1.1  christos 					UNEXPECTED_ERROR(__FILE__, __LINE__,
    612      1.1  christos 							 "%s: %u",
    613  1.1.1.4  christos 							 "couldn't schedule "
    614  1.1.1.4  christos 							 "timer",
    615      1.1  christos 							 result);
    616  1.1.1.4  christos 				}
    617      1.1  christos 			}
    618      1.1  christos 		} else {
    619      1.1  christos 			manager->due = timer->due;
    620  1.1.1.2  christos 			done = true;
    621      1.1  christos 		}
    622      1.1  christos 	}
    623      1.1  christos }
    624      1.1  christos 
    625      1.1  christos static isc_threadresult_t
    626  1.1.1.4  christos #ifdef _WIN32 /* XXXDCL */
    627  1.1.1.4  christos 	WINAPI
    628  1.1.1.4  christos #endif /* ifdef _WIN32 */
    629  1.1.1.4  christos 	run(void *uap) {
    630      1.1  christos 	isc__timermgr_t *manager = uap;
    631      1.1  christos 	isc_time_t now;
    632      1.1  christos 	isc_result_t result;
    633      1.1  christos 
    634      1.1  christos 	LOCK(&manager->lock);
    635      1.1  christos 	while (!manager->done) {
    636      1.1  christos 		TIME_NOW(&now);
    637      1.1  christos 
    638  1.1.1.3  christos 		XTRACETIME("running", now);
    639      1.1  christos 
    640      1.1  christos 		dispatch(manager, &now);
    641      1.1  christos 
    642      1.1  christos 		if (manager->nscheduled > 0) {
    643  1.1.1.3  christos 			XTRACETIME2("waituntil", manager->due, now);
    644  1.1.1.4  christos 			result = WAITUNTIL(&manager->wakeup, &manager->lock,
    645  1.1.1.4  christos 					   &manager->due);
    646      1.1  christos 			INSIST(result == ISC_R_SUCCESS ||
    647      1.1  christos 			       result == ISC_R_TIMEDOUT);
    648      1.1  christos 		} else {
    649  1.1.1.3  christos 			XTRACETIME("wait", now);
    650      1.1  christos 			WAIT(&manager->wakeup, &manager->lock);
    651      1.1  christos 		}
    652  1.1.1.3  christos 		XTRACE("wakeup");
    653      1.1  christos 	}
    654      1.1  christos 	UNLOCK(&manager->lock);
    655      1.1  christos 
    656      1.1  christos #ifdef OPENSSL_LEAKS
    657      1.1  christos 	ERR_remove_state(0);
    658  1.1.1.4  christos #endif /* ifdef OPENSSL_LEAKS */
    659      1.1  christos 
    660      1.1  christos 	return ((isc_threadresult_t)0);
    661      1.1  christos }
    662      1.1  christos 
    663  1.1.1.2  christos static bool
    664      1.1  christos sooner(void *v1, void *v2) {
    665      1.1  christos 	isc__timer_t *t1, *t2;
    666      1.1  christos 
    667      1.1  christos 	t1 = v1;
    668      1.1  christos 	t2 = v2;
    669      1.1  christos 	REQUIRE(VALID_TIMER(t1));
    670      1.1  christos 	REQUIRE(VALID_TIMER(t2));
    671      1.1  christos 
    672  1.1.1.4  christos 	if (isc_time_compare(&t1->due, &t2->due) < 0) {
    673  1.1.1.2  christos 		return (true);
    674  1.1.1.4  christos 	}
    675  1.1.1.2  christos 	return (false);
    676      1.1  christos }
    677      1.1  christos 
    678      1.1  christos static void
    679      1.1  christos set_index(void *what, unsigned int index) {
    680      1.1  christos 	isc__timer_t *timer;
    681      1.1  christos 
    682  1.1.1.4  christos 	REQUIRE(VALID_TIMER(what));
    683      1.1  christos 	timer = what;
    684      1.1  christos 
    685      1.1  christos 	timer->index = index;
    686      1.1  christos }
    687      1.1  christos 
    688      1.1  christos isc_result_t
    689  1.1.1.2  christos isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
    690      1.1  christos 	isc__timermgr_t *manager;
    691      1.1  christos 	isc_result_t result;
    692      1.1  christos 
    693      1.1  christos 	/*
    694      1.1  christos 	 * Create a timer manager.
    695      1.1  christos 	 */
    696      1.1  christos 
    697      1.1  christos 	REQUIRE(managerp != NULL && *managerp == NULL);
    698      1.1  christos 
    699      1.1  christos 	manager = isc_mem_get(mctx, sizeof(*manager));
    700      1.1  christos 
    701      1.1  christos 	manager->common.impmagic = TIMER_MANAGER_MAGIC;
    702      1.1  christos 	manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
    703      1.1  christos 	manager->mctx = NULL;
    704  1.1.1.2  christos 	manager->done = false;
    705      1.1  christos 	INIT_LIST(manager->timers);
    706      1.1  christos 	manager->nscheduled = 0;
    707      1.1  christos 	isc_time_settoepoch(&manager->due);
    708      1.1  christos 	manager->heap = NULL;
    709      1.1  christos 	result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
    710      1.1  christos 	if (result != ISC_R_SUCCESS) {
    711      1.1  christos 		INSIST(result == ISC_R_NOMEMORY);
    712      1.1  christos 		isc_mem_put(mctx, manager, sizeof(*manager));
    713      1.1  christos 		return (ISC_R_NOMEMORY);
    714      1.1  christos 	}
    715  1.1.1.2  christos 	isc_mutex_init(&manager->lock);
    716      1.1  christos 	isc_mem_attach(mctx, &manager->mctx);
    717  1.1.1.2  christos 	isc_condition_init(&manager->wakeup);
    718  1.1.1.4  christos 	isc_thread_create(run, manager, &manager->thread);
    719      1.1  christos 	isc_thread_setname(manager->thread, "isc-timer");
    720      1.1  christos 
    721      1.1  christos 	*managerp = (isc_timermgr_t *)manager;
    722      1.1  christos 
    723      1.1  christos 	return (ISC_R_SUCCESS);
    724      1.1  christos }
    725      1.1  christos 
    726      1.1  christos void
    727      1.1  christos isc_timermgr_poke(isc_timermgr_t *manager0) {
    728  1.1.1.4  christos 	isc__timermgr_t *manager;
    729      1.1  christos 
    730  1.1.1.4  christos 	REQUIRE(VALID_MANAGER(manager0));
    731  1.1.1.4  christos 	manager = (isc__timermgr_t *)manager0;
    732      1.1  christos 
    733      1.1  christos 	SIGNAL(&manager->wakeup);
    734      1.1  christos }
    735      1.1  christos 
    736      1.1  christos void
    737  1.1.1.2  christos isc_timermgr_destroy(isc_timermgr_t **managerp) {
    738      1.1  christos 	isc__timermgr_t *manager;
    739      1.1  christos 
    740      1.1  christos 	/*
    741      1.1  christos 	 * Destroy a timer manager.
    742      1.1  christos 	 */
    743      1.1  christos 
    744      1.1  christos 	REQUIRE(managerp != NULL);
    745      1.1  christos 	manager = (isc__timermgr_t *)*managerp;
    746      1.1  christos 	REQUIRE(VALID_MANAGER(manager));
    747      1.1  christos 
    748      1.1  christos 	LOCK(&manager->lock);
    749      1.1  christos 
    750      1.1  christos 	REQUIRE(EMPTY(manager->timers));
    751  1.1.1.2  christos 	manager->done = true;
    752      1.1  christos 
    753  1.1.1.3  christos 	XTRACE("signal (destroy)");
    754      1.1  christos 	SIGNAL(&manager->wakeup);
    755      1.1  christos 
    756      1.1  christos 	UNLOCK(&manager->lock);
    757      1.1  christos 
    758      1.1  christos 	/*
    759      1.1  christos 	 * Wait for thread to exit.
    760      1.1  christos 	 */
    761  1.1.1.4  christos 	isc_thread_join(manager->thread, NULL);
    762      1.1  christos 
    763      1.1  christos 	/*
    764      1.1  christos 	 * Clean up.
    765      1.1  christos 	 */
    766      1.1  christos 	(void)isc_condition_destroy(&manager->wakeup);
    767  1.1.1.2  christos 	isc_mutex_destroy(&manager->lock);
    768      1.1  christos 	isc_heap_destroy(&manager->heap);
    769      1.1  christos 	manager->common.impmagic = 0;
    770      1.1  christos 	manager->common.magic = 0;
    771  1.1.1.4  christos 	isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
    772      1.1  christos 
    773      1.1  christos 	*managerp = NULL;
    774      1.1  christos }
    775      1.1  christos 
    776      1.1  christos isc_result_t
    777  1.1.1.4  christos isc_timermgr_createinctx(isc_mem_t *mctx, isc_timermgr_t **managerp) {
    778      1.1  christos 	isc_result_t result;
    779      1.1  christos 
    780  1.1.1.2  christos 	result = isc_timermgr_create(mctx, managerp);
    781      1.1  christos 
    782      1.1  christos 	return (result);
    783      1.1  christos }
    784