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