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