15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
17*1fb015a8Sflorian /* $Id: timer.c,v 1.25 2020/09/14 08:40:44 florian Exp $ */
185185a700Sflorian
195185a700Sflorian /*! \file */
205185a700Sflorian
214465bcfbSjsg #include <sys/time.h>
225185a700Sflorian #include <stdlib.h>
234465bcfbSjsg #include <time.h>
245185a700Sflorian #include <isc/heap.h>
255185a700Sflorian #include <isc/task.h>
265185a700Sflorian #include <isc/timer.h>
275185a700Sflorian #include <isc/util.h>
285185a700Sflorian
295185a700Sflorian #include "timer_p.h"
305185a700Sflorian
318b553854Sflorian struct isc_timer {
325185a700Sflorian /*! Not locked. */
338b553854Sflorian isc_timermgr_t * manager;
345185a700Sflorian /*! Locked by timer lock. */
355185a700Sflorian unsigned int references;
367238a213Sflorian struct timespec idle;
375185a700Sflorian /*! Locked by manager lock. */
387238a213Sflorian struct timespec interval;
395185a700Sflorian isc_task_t * task;
405185a700Sflorian isc_taskaction_t action;
415185a700Sflorian void * arg;
425185a700Sflorian unsigned int index;
437238a213Sflorian struct timespec due;
448b553854Sflorian LINK(isc_timer_t) link;
455185a700Sflorian };
465185a700Sflorian
478b553854Sflorian struct isc_timermgr {
485185a700Sflorian /* Not locked. */
495185a700Sflorian /* Locked by manager lock. */
50*1fb015a8Sflorian int done;
518b553854Sflorian LIST(isc_timer_t) timers;
525185a700Sflorian unsigned int nscheduled;
537238a213Sflorian struct timespec due;
545185a700Sflorian unsigned int refs;
555185a700Sflorian isc_heap_t * heap;
565185a700Sflorian };
575185a700Sflorian
585185a700Sflorian /*%
595185a700Sflorian * The following are intended for internal use (indicated by "isc__"
605185a700Sflorian * prefix) but are not declared as static, allowing direct access from
615185a700Sflorian * unit tests etc.
625185a700Sflorian */
635185a700Sflorian
645185a700Sflorian /*!
655185a700Sflorian * If the manager is supposed to be shared, there can be only one.
665185a700Sflorian */
678b553854Sflorian static isc_timermgr_t *timermgr = NULL;
685185a700Sflorian
695185a700Sflorian static inline isc_result_t
schedule(isc_timer_t * timer)70ad5cf538Sjung schedule(isc_timer_t *timer) {
715185a700Sflorian isc_result_t result;
728b553854Sflorian isc_timermgr_t *manager;
737238a213Sflorian struct timespec due;
745185a700Sflorian
755185a700Sflorian /*!
765185a700Sflorian * Note: the caller must ensure locking.
775185a700Sflorian */
785185a700Sflorian
795185a700Sflorian manager = timer->manager;
805185a700Sflorian
815185a700Sflorian /*
825185a700Sflorian * Compute the new due time.
835185a700Sflorian */
845185a700Sflorian due = timer->idle;
855185a700Sflorian
865185a700Sflorian /*
875185a700Sflorian * Schedule the timer.
885185a700Sflorian */
895185a700Sflorian
905185a700Sflorian if (timer->index > 0) {
915185a700Sflorian /*
925185a700Sflorian * Already scheduled.
935185a700Sflorian */
94ba6f4614Sflorian if (timespeccmp(&due, &timer->due, <))
955185a700Sflorian isc_heap_increased(manager->heap, timer->index);
96ba6f4614Sflorian else if (timespeccmp(&due, &timer->due, >))
975185a700Sflorian isc_heap_decreased(manager->heap, timer->index);
98ba6f4614Sflorian
99ba6f4614Sflorian timer->due = due;
1005185a700Sflorian } else {
1015185a700Sflorian timer->due = due;
1025185a700Sflorian result = isc_heap_insert(manager->heap, timer);
1035185a700Sflorian if (result != ISC_R_SUCCESS) {
1045185a700Sflorian INSIST(result == ISC_R_NOMEMORY);
1055185a700Sflorian return (ISC_R_NOMEMORY);
1065185a700Sflorian }
1075185a700Sflorian manager->nscheduled++;
1085185a700Sflorian }
1095185a700Sflorian
1105185a700Sflorian /*
1115185a700Sflorian * If this timer is at the head of the queue, we need to ensure
1125185a700Sflorian * that we won't miss it if it has a more recent due time than
1135185a700Sflorian * the current "next" timer. We do this either by waking up the
1145185a700Sflorian * run thread, or explicitly setting the value in the manager.
1155185a700Sflorian */
116ba6f4614Sflorian if (timer->index == 1 && timespeccmp(&timer->due, &manager->due, <))
1175185a700Sflorian manager->due = timer->due;
1185185a700Sflorian
1195185a700Sflorian return (ISC_R_SUCCESS);
1205185a700Sflorian }
1215185a700Sflorian
1225185a700Sflorian static inline void
deschedule(isc_timer_t * timer)1238b553854Sflorian deschedule(isc_timer_t *timer) {
1248b553854Sflorian isc_timermgr_t *manager;
1255185a700Sflorian
1265185a700Sflorian /*
1275185a700Sflorian * The caller must ensure locking.
1285185a700Sflorian */
1295185a700Sflorian
1305185a700Sflorian manager = timer->manager;
1315185a700Sflorian if (timer->index > 0) {
1325185a700Sflorian isc_heap_delete(manager->heap, timer->index);
1335185a700Sflorian timer->index = 0;
1345185a700Sflorian INSIST(manager->nscheduled > 0);
1355185a700Sflorian manager->nscheduled--;
1365185a700Sflorian }
1375185a700Sflorian }
1385185a700Sflorian
1395185a700Sflorian static void
destroy(isc_timer_t * timer)1408b553854Sflorian destroy(isc_timer_t *timer) {
1418b553854Sflorian isc_timermgr_t *manager = timer->manager;
1425185a700Sflorian
1435185a700Sflorian /*
1445185a700Sflorian * The caller must ensure it is safe to destroy the timer.
1455185a700Sflorian */
1465185a700Sflorian
1475185a700Sflorian (void)isc_task_purgerange(timer->task,
1485185a700Sflorian timer,
1495185a700Sflorian ISC_TIMEREVENT_FIRSTEVENT,
1505185a700Sflorian ISC_TIMEREVENT_LASTEVENT,
1515185a700Sflorian NULL);
1525185a700Sflorian deschedule(timer);
1535185a700Sflorian UNLINK(manager->timers, timer, link);
1545185a700Sflorian
1555185a700Sflorian isc_task_detach(&timer->task);
1565185a700Sflorian free(timer);
1575185a700Sflorian }
1585185a700Sflorian
1595185a700Sflorian isc_result_t
isc_timer_create(isc_timermgr_t * manager0,const struct timespec * interval,isc_task_t * task,isc_taskaction_t action,void * arg,isc_timer_t ** timerp)1608b553854Sflorian isc_timer_create(isc_timermgr_t *manager0, const struct timespec *interval,
1615185a700Sflorian isc_task_t *task, isc_taskaction_t action, void *arg,
1625185a700Sflorian isc_timer_t **timerp)
1635185a700Sflorian {
1648b553854Sflorian isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
1658b553854Sflorian isc_timer_t *timer;
1665185a700Sflorian isc_result_t result;
1677238a213Sflorian struct timespec now;
1685185a700Sflorian
1695185a700Sflorian /*
1705185a700Sflorian * Create a new 'type' timer managed by 'manager'. The timers
1715ec36317Sflorian * parameters are specified by 'interval'. Events
1725185a700Sflorian * will be posted to 'task' and when dispatched 'action' will be
1735185a700Sflorian * called with 'arg' as the arg value. The new timer is returned
1745185a700Sflorian * in 'timerp'.
1755185a700Sflorian */
1765185a700Sflorian
1775185a700Sflorian REQUIRE(task != NULL);
1785185a700Sflorian REQUIRE(action != NULL);
1791eb893f3Sflorian REQUIRE(interval != NULL);
180396be909Sflorian REQUIRE(timespecisset(interval));
1815185a700Sflorian REQUIRE(timerp != NULL && *timerp == NULL);
1825185a700Sflorian
1835185a700Sflorian /*
1845185a700Sflorian * Get current time.
1855185a700Sflorian */
186b53d8310Sflorian clock_gettime(CLOCK_MONOTONIC, &now);
1875185a700Sflorian
1885185a700Sflorian timer = malloc(sizeof(*timer));
1895185a700Sflorian if (timer == NULL)
1905185a700Sflorian return (ISC_R_NOMEMORY);
1915185a700Sflorian
1925185a700Sflorian timer->manager = manager;
1935185a700Sflorian timer->references = 1;
1945185a700Sflorian
195ffbbf1a1Sflorian if (timespecisset(interval))
196ffbbf1a1Sflorian timespecadd(&now, interval, &timer->idle);
1975185a700Sflorian
1985185a700Sflorian timer->interval = *interval;
1995185a700Sflorian timer->task = NULL;
2005185a700Sflorian isc_task_attach(task, &timer->task);
2015185a700Sflorian timer->action = action;
2025185a700Sflorian /*
2035185a700Sflorian * Removing the const attribute from "arg" is the best of two
2045185a700Sflorian * evils here. If the timer->arg member is made const, then
2055185a700Sflorian * it affects a great many recipients of the timer event
2065185a700Sflorian * which did not pass in an "arg" that was truly const.
2075185a700Sflorian * Changing isc_timer_create() to not have "arg" prototyped as const,
2085185a700Sflorian * though, can cause compilers warnings for calls that *do*
2095185a700Sflorian * have a truly const arg. The caller will have to carefully
2105185a700Sflorian * keep track of whether arg started as a true const.
2115185a700Sflorian */
2125185a700Sflorian DE_CONST(arg, timer->arg);
2135185a700Sflorian timer->index = 0;
2145185a700Sflorian ISC_LINK_INIT(timer, link);
2155185a700Sflorian
216ad5cf538Sjung result = schedule(timer);
2175185a700Sflorian if (result == ISC_R_SUCCESS)
2185185a700Sflorian APPEND(manager->timers, timer, link);
2195185a700Sflorian
2205185a700Sflorian if (result != ISC_R_SUCCESS) {
2215185a700Sflorian isc_task_detach(&timer->task);
2225185a700Sflorian free(timer);
2235185a700Sflorian return (result);
2245185a700Sflorian }
2255185a700Sflorian
2265185a700Sflorian *timerp = (isc_timer_t *)timer;
2275185a700Sflorian
2285185a700Sflorian return (ISC_R_SUCCESS);
2295185a700Sflorian }
2305185a700Sflorian
2315185a700Sflorian isc_result_t
isc_timer_reset(isc_timer_t * timer,const struct timespec * interval,int purge)2328b553854Sflorian isc_timer_reset(isc_timer_t *timer, const struct timespec *interval,
233*1fb015a8Sflorian int purge)
2345185a700Sflorian {
2357238a213Sflorian struct timespec now;
2365185a700Sflorian isc_result_t result;
2375185a700Sflorian
2385185a700Sflorian /*
2395185a700Sflorian * Change the timer's type, expires, and interval values to the given
240*1fb015a8Sflorian * values. If 'purge' is 1, any pending events from this timer
2415185a700Sflorian * are purged from its task's event queue.
2425185a700Sflorian */
2435185a700Sflorian
2441eb893f3Sflorian REQUIRE(interval != NULL);
245396be909Sflorian REQUIRE(timespecisset(interval));
2465185a700Sflorian
2475185a700Sflorian /*
2485185a700Sflorian * Get current time.
2495185a700Sflorian */
250b53d8310Sflorian clock_gettime(CLOCK_MONOTONIC, &now);
2515185a700Sflorian
2525185a700Sflorian if (purge)
2535185a700Sflorian (void)isc_task_purgerange(timer->task,
2545185a700Sflorian timer,
2555185a700Sflorian ISC_TIMEREVENT_FIRSTEVENT,
2565185a700Sflorian ISC_TIMEREVENT_LASTEVENT,
2575185a700Sflorian NULL);
2585185a700Sflorian timer->interval = *interval;
259396be909Sflorian if (timespecisset(interval)) {
260ffbbf1a1Sflorian timespecadd(&now, interval, &timer->idle);
2615185a700Sflorian } else {
262bb7ec108Sflorian timespecclear(&timer->idle);
2635185a700Sflorian }
2645185a700Sflorian
265ad5cf538Sjung result = schedule(timer);
2665185a700Sflorian
2675185a700Sflorian return (result);
2685185a700Sflorian }
2695185a700Sflorian
270ffbbf1a1Sflorian void
isc_timer_touch(isc_timer_t * timer)2718b553854Sflorian isc_timer_touch(isc_timer_t *timer) {
2727238a213Sflorian struct timespec now;
2735185a700Sflorian
2745185a700Sflorian /*
2755185a700Sflorian * Set the last-touched time of 'timer' to the current time.
2765185a700Sflorian */
2775185a700Sflorian
278b53d8310Sflorian clock_gettime(CLOCK_MONOTONIC, &now);
279ffbbf1a1Sflorian timespecadd(&now, &timer->interval, &timer->idle);
2805185a700Sflorian }
2815185a700Sflorian
2825185a700Sflorian void
isc_timer_detach(isc_timer_t ** timerp)2838b553854Sflorian isc_timer_detach(isc_timer_t **timerp) {
2848b553854Sflorian isc_timer_t *timer;
285*1fb015a8Sflorian int free_timer = 0;
2865185a700Sflorian
2875185a700Sflorian /*
2885185a700Sflorian * Detach *timerp from its timer.
2895185a700Sflorian */
2905185a700Sflorian
2915185a700Sflorian REQUIRE(timerp != NULL);
2928b553854Sflorian timer = (isc_timer_t *)*timerp;
2935185a700Sflorian
2945185a700Sflorian REQUIRE(timer->references > 0);
2955185a700Sflorian timer->references--;
2965185a700Sflorian if (timer->references == 0)
297*1fb015a8Sflorian free_timer = 1;
2985185a700Sflorian
2995185a700Sflorian if (free_timer)
3005185a700Sflorian destroy(timer);
3015185a700Sflorian
3025185a700Sflorian *timerp = NULL;
3035185a700Sflorian }
3045185a700Sflorian
3055185a700Sflorian static void
dispatch(isc_timermgr_t * manager,struct timespec * now)3068b553854Sflorian dispatch(isc_timermgr_t *manager, struct timespec *now) {
307*1fb015a8Sflorian int done = 0, post_event, need_schedule;
3085185a700Sflorian isc_timerevent_t *event;
3095185a700Sflorian isc_eventtype_t type = 0;
3108b553854Sflorian isc_timer_t *timer;
3115185a700Sflorian isc_result_t result;
312*1fb015a8Sflorian int idle;
3135185a700Sflorian
3145185a700Sflorian /*!
3155185a700Sflorian * The caller must be holding the manager lock.
3165185a700Sflorian */
3175185a700Sflorian
3185185a700Sflorian while (manager->nscheduled > 0 && !done) {
3195185a700Sflorian timer = isc_heap_element(manager->heap, 1);
32075fe954bSflorian INSIST(timer != NULL);
321ba6f4614Sflorian if (timespeccmp(now, &timer->due, >=)) {
322*1fb015a8Sflorian idle = 0;
3235185a700Sflorian
324ba6f4614Sflorian if (timespecisset(&timer->idle) && timespeccmp(now,
325ba6f4614Sflorian &timer->idle, >=)) {
326*1fb015a8Sflorian idle = 1;
3275185a700Sflorian }
3285185a700Sflorian if (idle) {
3295185a700Sflorian type = ISC_TIMEREVENT_IDLE;
330*1fb015a8Sflorian post_event = 1;
331*1fb015a8Sflorian need_schedule = 0;
3325185a700Sflorian } else {
3335185a700Sflorian /*
3345185a700Sflorian * Idle timer has been touched;
3355185a700Sflorian * reschedule.
3365185a700Sflorian */
337*1fb015a8Sflorian post_event = 0;
338*1fb015a8Sflorian need_schedule = 1;
3395185a700Sflorian }
3405185a700Sflorian
3415185a700Sflorian if (post_event) {
3425185a700Sflorian /*
3435185a700Sflorian * XXX We could preallocate this event.
3445185a700Sflorian */
3455185a700Sflorian event = (isc_timerevent_t *)isc_event_allocate(
3465185a700Sflorian timer,
3475185a700Sflorian type,
3485185a700Sflorian timer->action,
3495185a700Sflorian timer->arg,
3505185a700Sflorian sizeof(*event));
3515185a700Sflorian
3525185a700Sflorian if (event != NULL) {
3535185a700Sflorian event->due = timer->due;
3545185a700Sflorian isc_task_send(timer->task,
3555185a700Sflorian ISC_EVENT_PTR(&event));
3565185a700Sflorian } else
3575185a700Sflorian UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
3585185a700Sflorian "couldn't allocate event");
3595185a700Sflorian }
3605185a700Sflorian
3615185a700Sflorian timer->index = 0;
3625185a700Sflorian isc_heap_delete(manager->heap, 1);
3635185a700Sflorian manager->nscheduled--;
3645185a700Sflorian
3655185a700Sflorian if (need_schedule) {
366ad5cf538Sjung result = schedule(timer);
3675185a700Sflorian if (result != ISC_R_SUCCESS)
3685185a700Sflorian UNEXPECTED_ERROR(__FILE__, __LINE__,
3695185a700Sflorian "%s: %u",
3705185a700Sflorian "couldn't schedule timer",
3715185a700Sflorian result);
3725185a700Sflorian }
3735185a700Sflorian } else {
3745185a700Sflorian manager->due = timer->due;
375*1fb015a8Sflorian done = 1;
3765185a700Sflorian }
3775185a700Sflorian }
3785185a700Sflorian }
3795185a700Sflorian
380*1fb015a8Sflorian static int
sooner(void * v1,void * v2)3815185a700Sflorian sooner(void *v1, void *v2) {
3828b553854Sflorian isc_timer_t *t1, *t2;
3835185a700Sflorian
3845185a700Sflorian t1 = v1;
3855185a700Sflorian t2 = v2;
3865185a700Sflorian
387ba6f4614Sflorian if (timespeccmp(&t1->due, &t2->due, <))
388*1fb015a8Sflorian return (1);
389*1fb015a8Sflorian return (0);
3905185a700Sflorian }
3915185a700Sflorian
3925185a700Sflorian static void
set_index(void * what,unsigned int index)3935185a700Sflorian set_index(void *what, unsigned int index) {
3948b553854Sflorian isc_timer_t *timer;
3955185a700Sflorian
3965185a700Sflorian timer = what;
3975185a700Sflorian
3985185a700Sflorian timer->index = index;
3995185a700Sflorian }
4005185a700Sflorian
4015185a700Sflorian isc_result_t
isc_timermgr_create(isc_timermgr_t ** managerp)4028b553854Sflorian isc_timermgr_create(isc_timermgr_t **managerp) {
4038b553854Sflorian isc_timermgr_t *manager;
4045185a700Sflorian isc_result_t result;
4055185a700Sflorian
4065185a700Sflorian /*
4075185a700Sflorian * Create a timer manager.
4085185a700Sflorian */
4095185a700Sflorian
4105185a700Sflorian REQUIRE(managerp != NULL && *managerp == NULL);
4115185a700Sflorian
4125185a700Sflorian if (timermgr != NULL) {
4135185a700Sflorian timermgr->refs++;
4145185a700Sflorian *managerp = (isc_timermgr_t *)timermgr;
4155185a700Sflorian return (ISC_R_SUCCESS);
4165185a700Sflorian }
4175185a700Sflorian
4185185a700Sflorian manager = malloc(sizeof(*manager));
4195185a700Sflorian if (manager == NULL)
4205185a700Sflorian return (ISC_R_NOMEMORY);
4215185a700Sflorian
422*1fb015a8Sflorian manager->done = 0;
4235185a700Sflorian INIT_LIST(manager->timers);
4245185a700Sflorian manager->nscheduled = 0;
425bb7ec108Sflorian timespecclear(&manager->due);
4265185a700Sflorian manager->heap = NULL;
4275185a700Sflorian result = isc_heap_create(sooner, set_index, 0, &manager->heap);
4285185a700Sflorian if (result != ISC_R_SUCCESS) {
4295185a700Sflorian INSIST(result == ISC_R_NOMEMORY);
4305185a700Sflorian free(manager);
4315185a700Sflorian return (ISC_R_NOMEMORY);
4325185a700Sflorian }
4335185a700Sflorian manager->refs = 1;
4345185a700Sflorian timermgr = manager;
4355185a700Sflorian
4365185a700Sflorian *managerp = (isc_timermgr_t *)manager;
4375185a700Sflorian
4385185a700Sflorian return (ISC_R_SUCCESS);
4395185a700Sflorian }
4405185a700Sflorian
4415185a700Sflorian void
isc_timermgr_destroy(isc_timermgr_t ** managerp)4428b553854Sflorian isc_timermgr_destroy(isc_timermgr_t **managerp) {
4438b553854Sflorian isc_timermgr_t *manager;
4445185a700Sflorian
4455185a700Sflorian /*
4465185a700Sflorian * Destroy a timer manager.
4475185a700Sflorian */
4485185a700Sflorian
4495185a700Sflorian REQUIRE(managerp != NULL);
4508b553854Sflorian manager = (isc_timermgr_t *)*managerp;
4515185a700Sflorian
4525185a700Sflorian manager->refs--;
4535185a700Sflorian if (manager->refs > 0) {
4545185a700Sflorian *managerp = NULL;
4555185a700Sflorian return;
4565185a700Sflorian }
4575185a700Sflorian timermgr = NULL;
4585185a700Sflorian
4598b553854Sflorian isc_timermgr_dispatch((isc_timermgr_t *)manager);
4605185a700Sflorian
4615185a700Sflorian REQUIRE(EMPTY(manager->timers));
462*1fb015a8Sflorian manager->done = 1;
4635185a700Sflorian
4645185a700Sflorian /*
4655185a700Sflorian * Clean up.
4665185a700Sflorian */
4675185a700Sflorian isc_heap_destroy(&manager->heap);
4685185a700Sflorian free(manager);
4695185a700Sflorian
4705185a700Sflorian *managerp = NULL;
4715185a700Sflorian
4725185a700Sflorian timermgr = NULL;
4735185a700Sflorian }
4745185a700Sflorian
4755185a700Sflorian isc_result_t
isc_timermgr_nextevent(isc_timermgr_t * manager0,struct timespec * when)4768b553854Sflorian isc_timermgr_nextevent(isc_timermgr_t *manager0, struct timespec *when) {
4778b553854Sflorian isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
4785185a700Sflorian
4795185a700Sflorian if (manager == NULL)
4805185a700Sflorian manager = timermgr;
4815185a700Sflorian if (manager == NULL || manager->nscheduled == 0)
4825185a700Sflorian return (ISC_R_NOTFOUND);
4835185a700Sflorian *when = manager->due;
4845185a700Sflorian return (ISC_R_SUCCESS);
4855185a700Sflorian }
4865185a700Sflorian
4875185a700Sflorian void
isc_timermgr_dispatch(isc_timermgr_t * manager0)4888b553854Sflorian isc_timermgr_dispatch(isc_timermgr_t *manager0) {
4898b553854Sflorian isc_timermgr_t *manager = (isc_timermgr_t *)manager0;
4907238a213Sflorian struct timespec now;
4915185a700Sflorian
4925185a700Sflorian if (manager == NULL)
4935185a700Sflorian manager = timermgr;
4945185a700Sflorian if (manager == NULL)
4955185a700Sflorian return;
496b53d8310Sflorian clock_gettime(CLOCK_MONOTONIC, &now);
4975185a700Sflorian dispatch(manager, &now);
4985185a700Sflorian }
4995185a700Sflorian
500