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