1: /*
2: * Copyright (C) 2004, 2005, 2007-2009 Internet Systems Consortium, Inc. ("ISC")
3: * Copyright (C) 1998-2002 Internet Software Consortium.
4: *
5: * Permission to use, copy, modify, and/or distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10: * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11: * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12: * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13: * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14: * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15: * PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: /* $Id: timer.c,v 1.1.1.1 2012/05/29 12:08:38 misho Exp $ */
19:
20: /*! \file */
21:
22: #include <config.h>
23:
24: #include <isc/condition.h>
25: #include <isc/heap.h>
26: #include <isc/log.h>
27: #include <isc/magic.h>
28: #include <isc/mem.h>
29: #include <isc/msgs.h>
30: #include <isc/platform.h>
31: #include <isc/task.h>
32: #include <isc/thread.h>
33: #include <isc/time.h>
34: #include <isc/timer.h>
35: #include <isc/util.h>
36:
37: #ifndef ISC_PLATFORM_USETHREADS
38: #include "timer_p.h"
39: #endif /* ISC_PLATFORM_USETHREADS */
40:
41: #ifdef ISC_TIMER_TRACE
42: #define XTRACE(s) fprintf(stderr, "%s\n", (s))
43: #define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
44: #define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \
45: (d).seconds, (d).nanoseconds)
46: #define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
47: (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
48: #define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
49: (d).seconds, (d).nanoseconds)
50: #else
51: #define XTRACE(s)
52: #define XTRACEID(s, t)
53: #define XTRACETIME(s, d)
54: #define XTRACETIME2(s, d, n)
55: #define XTRACETIMER(s, t, d)
56: #endif /* ISC_TIMER_TRACE */
57:
58: #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
59: #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
60:
61: struct isc_timer {
62: /*! Not locked. */
63: unsigned int magic;
64: isc_timermgr_t * manager;
65: isc_mutex_t lock;
66: /*! Locked by timer lock. */
67: unsigned int references;
68: isc_time_t idle;
69: /*! Locked by manager lock. */
70: isc_timertype_t type;
71: isc_time_t expires;
72: isc_interval_t interval;
73: isc_task_t * task;
74: isc_taskaction_t action;
75: void * arg;
76: unsigned int index;
77: isc_time_t due;
78: LINK(isc_timer_t) link;
79: };
80:
81: #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
82: #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
83:
84: struct isc_timermgr {
85: /* Not locked. */
86: unsigned int magic;
87: isc_mem_t * mctx;
88: isc_mutex_t lock;
89: /* Locked by manager lock. */
90: isc_boolean_t done;
91: LIST(isc_timer_t) timers;
92: unsigned int nscheduled;
93: isc_time_t due;
94: #ifdef ISC_PLATFORM_USETHREADS
95: isc_condition_t wakeup;
96: isc_thread_t thread;
97: #else /* ISC_PLATFORM_USETHREADS */
98: unsigned int refs;
99: #endif /* ISC_PLATFORM_USETHREADS */
100: isc_heap_t * heap;
101: };
102:
103: #ifndef ISC_PLATFORM_USETHREADS
104: /*!
105: * If threads are not in use, there can be only one.
106: */
107: static isc_timermgr_t *timermgr = NULL;
108: #endif /* ISC_PLATFORM_USETHREADS */
109:
110: static inline isc_result_t
111: schedule(isc_timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
112: isc_result_t result;
113: isc_timermgr_t *manager;
114: isc_time_t due;
115: int cmp;
116: #ifdef ISC_PLATFORM_USETHREADS
117: isc_boolean_t timedwait;
118: #endif
119:
120: /*!
121: * Note: the caller must ensure locking.
122: */
123:
124: REQUIRE(timer->type != isc_timertype_inactive);
125:
126: #ifndef ISC_PLATFORM_USETHREADS
127: UNUSED(signal_ok);
128: #endif /* ISC_PLATFORM_USETHREADS */
129:
130: manager = timer->manager;
131:
132: #ifdef ISC_PLATFORM_USETHREADS
133: /*!
134: * If the manager was timed wait, we may need to signal the
135: * manager to force a wakeup.
136: */
137: timedwait = ISC_TF(manager->nscheduled > 0 &&
138: isc_time_seconds(&manager->due) != 0);
139: #endif
140:
141: /*
142: * Compute the new due time.
143: */
144: if (timer->type != isc_timertype_once) {
145: result = isc_time_add(now, &timer->interval, &due);
146: if (result != ISC_R_SUCCESS)
147: return (result);
148: if (timer->type == isc_timertype_limited &&
149: isc_time_compare(&timer->expires, &due) < 0)
150: due = timer->expires;
151: } else {
152: if (isc_time_isepoch(&timer->idle))
153: due = timer->expires;
154: else if (isc_time_isepoch(&timer->expires))
155: due = timer->idle;
156: else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
157: due = timer->idle;
158: else
159: due = timer->expires;
160: }
161:
162: /*
163: * Schedule the timer.
164: */
165:
166: if (timer->index > 0) {
167: /*
168: * Already scheduled.
169: */
170: cmp = isc_time_compare(&due, &timer->due);
171: timer->due = due;
172: switch (cmp) {
173: case -1:
174: isc_heap_increased(manager->heap, timer->index);
175: break;
176: case 1:
177: isc_heap_decreased(manager->heap, timer->index);
178: break;
179: case 0:
180: /* Nothing to do. */
181: break;
182: }
183: } else {
184: timer->due = due;
185: result = isc_heap_insert(manager->heap, timer);
186: if (result != ISC_R_SUCCESS) {
187: INSIST(result == ISC_R_NOMEMORY);
188: return (ISC_R_NOMEMORY);
189: }
190: manager->nscheduled++;
191: }
192:
193: XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
194: ISC_MSG_SCHEDULE, "schedule"), timer, due);
195:
196: /*
197: * If this timer is at the head of the queue, we need to ensure
198: * that we won't miss it if it has a more recent due time than
199: * the current "next" timer. We do this either by waking up the
200: * run thread, or explicitly setting the value in the manager.
201: */
202: #ifdef ISC_PLATFORM_USETHREADS
203:
204: /*
205: * This is a temporary (probably) hack to fix a bug on tru64 5.1
206: * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
207: * return when the time expires, so here, we check to see if
208: * we're 15 seconds or more behind, and if we are, we signal
209: * the dispatcher. This isn't such a bad idea as a general purpose
210: * watchdog, so perhaps we should just leave it in here.
211: */
212: if (signal_ok && timedwait) {
213: isc_interval_t fifteen;
214: isc_time_t then;
215:
216: isc_interval_set(&fifteen, 15, 0);
217: result = isc_time_add(&manager->due, &fifteen, &then);
218:
219: if (result == ISC_R_SUCCESS &&
220: isc_time_compare(&then, now) < 0) {
221: SIGNAL(&manager->wakeup);
222: signal_ok = ISC_FALSE;
223: isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
224: ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
225: "*** POKED TIMER ***");
226: }
227: }
228:
229: if (timer->index == 1 && signal_ok) {
230: XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
231: ISC_MSG_SIGNALSCHED,
232: "signal (schedule)"));
233: SIGNAL(&manager->wakeup);
234: }
235: #else /* ISC_PLATFORM_USETHREADS */
236: if (timer->index == 1 &&
237: isc_time_compare(&timer->due, &manager->due) < 0)
238: manager->due = timer->due;
239: #endif /* ISC_PLATFORM_USETHREADS */
240:
241: return (ISC_R_SUCCESS);
242: }
243:
244: static inline void
245: deschedule(isc_timer_t *timer) {
246: isc_boolean_t need_wakeup = ISC_FALSE;
247: isc_timermgr_t *manager;
248:
249: /*
250: * The caller must ensure locking.
251: */
252:
253: manager = timer->manager;
254: if (timer->index > 0) {
255: if (timer->index == 1)
256: need_wakeup = ISC_TRUE;
257: isc_heap_delete(manager->heap, timer->index);
258: timer->index = 0;
259: INSIST(manager->nscheduled > 0);
260: manager->nscheduled--;
261: #ifdef ISC_PLATFORM_USETHREADS
262: if (need_wakeup) {
263: XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
264: ISC_MSG_SIGNALDESCHED,
265: "signal (deschedule)"));
266: SIGNAL(&manager->wakeup);
267: }
268: #endif /* ISC_PLATFORM_USETHREADS */
269: }
270: }
271:
272: static void
273: destroy(isc_timer_t *timer) {
274: isc_timermgr_t *manager = timer->manager;
275:
276: /*
277: * The caller must ensure it is safe to destroy the timer.
278: */
279:
280: LOCK(&manager->lock);
281:
282: (void)isc_task_purgerange(timer->task,
283: timer,
284: ISC_TIMEREVENT_FIRSTEVENT,
285: ISC_TIMEREVENT_LASTEVENT,
286: NULL);
287: deschedule(timer);
288: UNLINK(manager->timers, timer, link);
289:
290: UNLOCK(&manager->lock);
291:
292: isc_task_detach(&timer->task);
293: DESTROYLOCK(&timer->lock);
294: timer->magic = 0;
295: isc_mem_put(manager->mctx, timer, sizeof(*timer));
296: }
297:
298: isc_result_t
299: isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
300: isc_time_t *expires, isc_interval_t *interval,
301: isc_task_t *task, isc_taskaction_t action, const void *arg,
302: isc_timer_t **timerp)
303: {
304: isc_timer_t *timer;
305: isc_result_t result;
306: isc_time_t now;
307:
308: /*
309: * Create a new 'type' timer managed by 'manager'. The timers
310: * parameters are specified by 'expires' and 'interval'. Events
311: * will be posted to 'task' and when dispatched 'action' will be
312: * called with 'arg' as the arg value. The new timer is returned
313: * in 'timerp'.
314: */
315:
316: REQUIRE(VALID_MANAGER(manager));
317: REQUIRE(task != NULL);
318: REQUIRE(action != NULL);
319: if (expires == NULL)
320: expires = isc_time_epoch;
321: if (interval == NULL)
322: interval = isc_interval_zero;
323: REQUIRE(type == isc_timertype_inactive ||
324: !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
325: REQUIRE(timerp != NULL && *timerp == NULL);
326: REQUIRE(type != isc_timertype_limited ||
327: !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
328:
329: /*
330: * Get current time.
331: */
332: if (type != isc_timertype_inactive) {
333: TIME_NOW(&now);
334: } else {
335: /*
336: * We don't have to do this, but it keeps the compiler from
337: * complaining about "now" possibly being used without being
338: * set, even though it will never actually happen.
339: */
340: isc_time_settoepoch(&now);
341: }
342:
343:
344: timer = isc_mem_get(manager->mctx, sizeof(*timer));
345: if (timer == NULL)
346: return (ISC_R_NOMEMORY);
347:
348: timer->manager = manager;
349: timer->references = 1;
350:
351: if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
352: result = isc_time_add(&now, interval, &timer->idle);
353: if (result != ISC_R_SUCCESS) {
354: isc_mem_put(manager->mctx, timer, sizeof(*timer));
355: return (result);
356: }
357: } else
358: isc_time_settoepoch(&timer->idle);
359:
360: timer->type = type;
361: timer->expires = *expires;
362: timer->interval = *interval;
363: timer->task = NULL;
364: isc_task_attach(task, &timer->task);
365: timer->action = action;
366: /*
367: * Removing the const attribute from "arg" is the best of two
368: * evils here. If the timer->arg member is made const, then
369: * it affects a great many recipients of the timer event
370: * which did not pass in an "arg" that was truly const.
371: * Changing isc_timer_create() to not have "arg" prototyped as const,
372: * though, can cause compilers warnings for calls that *do*
373: * have a truly const arg. The caller will have to carefully
374: * keep track of whether arg started as a true const.
375: */
376: DE_CONST(arg, timer->arg);
377: timer->index = 0;
378: result = isc_mutex_init(&timer->lock);
379: if (result != ISC_R_SUCCESS) {
380: isc_task_detach(&timer->task);
381: isc_mem_put(manager->mctx, timer, sizeof(*timer));
382: return (result);
383: }
384: ISC_LINK_INIT(timer, link);
385: timer->magic = TIMER_MAGIC;
386:
387: LOCK(&manager->lock);
388:
389: /*
390: * Note we don't have to lock the timer like we normally would because
391: * there are no external references to it yet.
392: */
393:
394: if (type != isc_timertype_inactive)
395: result = schedule(timer, &now, ISC_TRUE);
396: else
397: result = ISC_R_SUCCESS;
398: if (result == ISC_R_SUCCESS)
399: APPEND(manager->timers, timer, link);
400:
401: UNLOCK(&manager->lock);
402:
403: if (result != ISC_R_SUCCESS) {
404: timer->magic = 0;
405: DESTROYLOCK(&timer->lock);
406: isc_task_detach(&timer->task);
407: isc_mem_put(manager->mctx, timer, sizeof(*timer));
408: return (result);
409: }
410:
411: *timerp = timer;
412:
413: return (ISC_R_SUCCESS);
414: }
415:
416: isc_result_t
417: isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
418: isc_time_t *expires, isc_interval_t *interval,
419: isc_boolean_t purge)
420: {
421: isc_time_t now;
422: isc_timermgr_t *manager;
423: isc_result_t result;
424:
425: /*
426: * Change the timer's type, expires, and interval values to the given
427: * values. If 'purge' is ISC_TRUE, any pending events from this timer
428: * are purged from its task's event queue.
429: */
430:
431: REQUIRE(VALID_TIMER(timer));
432: manager = timer->manager;
433: REQUIRE(VALID_MANAGER(manager));
434: if (expires == NULL)
435: expires = isc_time_epoch;
436: if (interval == NULL)
437: interval = isc_interval_zero;
438: REQUIRE(type == isc_timertype_inactive ||
439: !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
440: REQUIRE(type != isc_timertype_limited ||
441: !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
442:
443: /*
444: * Get current time.
445: */
446: if (type != isc_timertype_inactive) {
447: TIME_NOW(&now);
448: } else {
449: /*
450: * We don't have to do this, but it keeps the compiler from
451: * complaining about "now" possibly being used without being
452: * set, even though it will never actually happen.
453: */
454: isc_time_settoepoch(&now);
455: }
456:
457: manager = timer->manager;
458:
459: LOCK(&manager->lock);
460: LOCK(&timer->lock);
461:
462: if (purge)
463: (void)isc_task_purgerange(timer->task,
464: timer,
465: ISC_TIMEREVENT_FIRSTEVENT,
466: ISC_TIMEREVENT_LASTEVENT,
467: NULL);
468: timer->type = type;
469: timer->expires = *expires;
470: timer->interval = *interval;
471: if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
472: result = isc_time_add(&now, interval, &timer->idle);
473: } else {
474: isc_time_settoepoch(&timer->idle);
475: result = ISC_R_SUCCESS;
476: }
477:
478: if (result == ISC_R_SUCCESS) {
479: if (type == isc_timertype_inactive) {
480: deschedule(timer);
481: result = ISC_R_SUCCESS;
482: } else
483: result = schedule(timer, &now, ISC_TRUE);
484: }
485:
486: UNLOCK(&timer->lock);
487: UNLOCK(&manager->lock);
488:
489: return (result);
490: }
491:
492: isc_timertype_t
493: isc_timer_gettype(isc_timer_t *timer) {
494: isc_timertype_t t;
495:
496: REQUIRE(VALID_TIMER(timer));
497:
498: LOCK(&timer->lock);
499: t = timer->type;
500: UNLOCK(&timer->lock);
501:
502: return (t);
503: }
504:
505: isc_result_t
506: isc_timer_touch(isc_timer_t *timer) {
507: isc_result_t result;
508: isc_time_t now;
509:
510: /*
511: * Set the last-touched time of 'timer' to the current time.
512: */
513:
514: REQUIRE(VALID_TIMER(timer));
515:
516: LOCK(&timer->lock);
517:
518: /*
519: * We'd like to
520: *
521: * REQUIRE(timer->type == isc_timertype_once);
522: *
523: * but we cannot without locking the manager lock too, which we
524: * don't want to do.
525: */
526:
527: TIME_NOW(&now);
528: result = isc_time_add(&now, &timer->interval, &timer->idle);
529:
530: UNLOCK(&timer->lock);
531:
532: return (result);
533: }
534:
535: void
536: isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
537: /*
538: * Attach *timerp to timer.
539: */
540:
541: REQUIRE(VALID_TIMER(timer));
542: REQUIRE(timerp != NULL && *timerp == NULL);
543:
544: LOCK(&timer->lock);
545: timer->references++;
546: UNLOCK(&timer->lock);
547:
548: *timerp = timer;
549: }
550:
551: void
552: isc_timer_detach(isc_timer_t **timerp) {
553: isc_timer_t *timer;
554: isc_boolean_t free_timer = ISC_FALSE;
555:
556: /*
557: * Detach *timerp from its timer.
558: */
559:
560: REQUIRE(timerp != NULL);
561: timer = *timerp;
562: REQUIRE(VALID_TIMER(timer));
563:
564: LOCK(&timer->lock);
565: REQUIRE(timer->references > 0);
566: timer->references--;
567: if (timer->references == 0)
568: free_timer = ISC_TRUE;
569: UNLOCK(&timer->lock);
570:
571: if (free_timer)
572: destroy(timer);
573:
574: *timerp = NULL;
575: }
576:
577: static void
578: dispatch(isc_timermgr_t *manager, isc_time_t *now) {
579: isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
580: isc_timerevent_t *event;
581: isc_eventtype_t type = 0;
582: isc_timer_t *timer;
583: isc_result_t result;
584: isc_boolean_t idle;
585:
586: /*!
587: * The caller must be holding the manager lock.
588: */
589:
590: while (manager->nscheduled > 0 && !done) {
591: timer = isc_heap_element(manager->heap, 1);
592: INSIST(timer->type != isc_timertype_inactive);
593: if (isc_time_compare(now, &timer->due) >= 0) {
594: if (timer->type == isc_timertype_ticker) {
595: type = ISC_TIMEREVENT_TICK;
596: post_event = ISC_TRUE;
597: need_schedule = ISC_TRUE;
598: } else if (timer->type == isc_timertype_limited) {
599: int cmp;
600: cmp = isc_time_compare(now, &timer->expires);
601: if (cmp >= 0) {
602: type = ISC_TIMEREVENT_LIFE;
603: post_event = ISC_TRUE;
604: need_schedule = ISC_FALSE;
605: } else {
606: type = ISC_TIMEREVENT_TICK;
607: post_event = ISC_TRUE;
608: need_schedule = ISC_TRUE;
609: }
610: } else if (!isc_time_isepoch(&timer->expires) &&
611: isc_time_compare(now,
612: &timer->expires) >= 0) {
613: type = ISC_TIMEREVENT_LIFE;
614: post_event = ISC_TRUE;
615: need_schedule = ISC_FALSE;
616: } else {
617: idle = ISC_FALSE;
618:
619: LOCK(&timer->lock);
620: if (!isc_time_isepoch(&timer->idle) &&
621: isc_time_compare(now,
622: &timer->idle) >= 0) {
623: idle = ISC_TRUE;
624: }
625: UNLOCK(&timer->lock);
626: if (idle) {
627: type = ISC_TIMEREVENT_IDLE;
628: post_event = ISC_TRUE;
629: need_schedule = ISC_FALSE;
630: } else {
631: /*
632: * Idle timer has been touched;
633: * reschedule.
634: */
635: XTRACEID(isc_msgcat_get(isc_msgcat,
636: ISC_MSGSET_TIMER,
637: ISC_MSG_IDLERESCHED,
638: "idle reschedule"),
639: timer);
640: post_event = ISC_FALSE;
641: need_schedule = ISC_TRUE;
642: }
643: }
644:
645: if (post_event) {
646: XTRACEID(isc_msgcat_get(isc_msgcat,
647: ISC_MSGSET_TIMER,
648: ISC_MSG_POSTING,
649: "posting"), timer);
650: /*
651: * XXX We could preallocate this event.
652: */
653: event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
654: timer,
655: type,
656: timer->action,
657: timer->arg,
658: sizeof(*event));
659:
660: if (event != NULL) {
661: event->due = timer->due;
662: isc_task_send(timer->task,
663: ISC_EVENT_PTR(&event));
664: } else
665: UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
666: isc_msgcat_get(isc_msgcat,
667: ISC_MSGSET_TIMER,
668: ISC_MSG_EVENTNOTALLOC,
669: "couldn't "
670: "allocate event"));
671: }
672:
673: timer->index = 0;
674: isc_heap_delete(manager->heap, 1);
675: manager->nscheduled--;
676:
677: if (need_schedule) {
678: result = schedule(timer, now, ISC_FALSE);
679: if (result != ISC_R_SUCCESS)
680: UNEXPECTED_ERROR(__FILE__, __LINE__,
681: "%s: %u",
682: isc_msgcat_get(isc_msgcat,
683: ISC_MSGSET_TIMER,
684: ISC_MSG_SCHEDFAIL,
685: "couldn't schedule "
686: "timer"),
687: result);
688: }
689: } else {
690: manager->due = timer->due;
691: done = ISC_TRUE;
692: }
693: }
694: }
695:
696: #ifdef ISC_PLATFORM_USETHREADS
697: static isc_threadresult_t
698: #ifdef _WIN32 /* XXXDCL */
699: WINAPI
700: #endif
701: run(void *uap) {
702: isc_timermgr_t *manager = uap;
703: isc_time_t now;
704: isc_result_t result;
705:
706: LOCK(&manager->lock);
707: while (!manager->done) {
708: TIME_NOW(&now);
709:
710: XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
711: ISC_MSG_RUNNING,
712: "running"), now);
713:
714: dispatch(manager, &now);
715:
716: if (manager->nscheduled > 0) {
717: XTRACETIME2(isc_msgcat_get(isc_msgcat,
718: ISC_MSGSET_GENERAL,
719: ISC_MSG_WAITUNTIL,
720: "waituntil"),
721: manager->due, now);
722: result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
723: INSIST(result == ISC_R_SUCCESS ||
724: result == ISC_R_TIMEDOUT);
725: } else {
726: XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
727: ISC_MSG_WAIT, "wait"), now);
728: WAIT(&manager->wakeup, &manager->lock);
729: }
730: XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
731: ISC_MSG_WAKEUP, "wakeup"));
732: }
733: UNLOCK(&manager->lock);
734:
735: return ((isc_threadresult_t)0);
736: }
737: #endif /* ISC_PLATFORM_USETHREADS */
738:
739: static isc_boolean_t
740: sooner(void *v1, void *v2) {
741: isc_timer_t *t1, *t2;
742:
743: t1 = v1;
744: t2 = v2;
745: REQUIRE(VALID_TIMER(t1));
746: REQUIRE(VALID_TIMER(t2));
747:
748: if (isc_time_compare(&t1->due, &t2->due) < 0)
749: return (ISC_TRUE);
750: return (ISC_FALSE);
751: }
752:
753: static void
754: set_index(void *what, unsigned int index) {
755: isc_timer_t *timer;
756:
757: timer = what;
758: REQUIRE(VALID_TIMER(timer));
759:
760: timer->index = index;
761: }
762:
763: isc_result_t
764: isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
765: isc_timermgr_t *manager;
766: isc_result_t result;
767:
768: /*
769: * Create a timer manager.
770: */
771:
772: REQUIRE(managerp != NULL && *managerp == NULL);
773:
774: #ifndef ISC_PLATFORM_USETHREADS
775: if (timermgr != NULL) {
776: timermgr->refs++;
777: *managerp = timermgr;
778: return (ISC_R_SUCCESS);
779: }
780: #endif /* ISC_PLATFORM_USETHREADS */
781:
782: manager = isc_mem_get(mctx, sizeof(*manager));
783: if (manager == NULL)
784: return (ISC_R_NOMEMORY);
785:
786: manager->magic = TIMER_MANAGER_MAGIC;
787: manager->mctx = NULL;
788: manager->done = ISC_FALSE;
789: INIT_LIST(manager->timers);
790: manager->nscheduled = 0;
791: isc_time_settoepoch(&manager->due);
792: manager->heap = NULL;
793: result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
794: if (result != ISC_R_SUCCESS) {
795: INSIST(result == ISC_R_NOMEMORY);
796: isc_mem_put(mctx, manager, sizeof(*manager));
797: return (ISC_R_NOMEMORY);
798: }
799: result = isc_mutex_init(&manager->lock);
800: if (result != ISC_R_SUCCESS) {
801: isc_heap_destroy(&manager->heap);
802: isc_mem_put(mctx, manager, sizeof(*manager));
803: return (result);
804: }
805: isc_mem_attach(mctx, &manager->mctx);
806: #ifdef ISC_PLATFORM_USETHREADS
807: if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
808: isc_mem_detach(&manager->mctx);
809: DESTROYLOCK(&manager->lock);
810: isc_heap_destroy(&manager->heap);
811: isc_mem_put(mctx, manager, sizeof(*manager));
812: UNEXPECTED_ERROR(__FILE__, __LINE__,
813: "isc_condition_init() %s",
814: isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
815: ISC_MSG_FAILED, "failed"));
816: return (ISC_R_UNEXPECTED);
817: }
818: if (isc_thread_create(run, manager, &manager->thread) !=
819: ISC_R_SUCCESS) {
820: isc_mem_detach(&manager->mctx);
821: (void)isc_condition_destroy(&manager->wakeup);
822: DESTROYLOCK(&manager->lock);
823: isc_heap_destroy(&manager->heap);
824: isc_mem_put(mctx, manager, sizeof(*manager));
825: UNEXPECTED_ERROR(__FILE__, __LINE__,
826: "isc_thread_create() %s",
827: isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
828: ISC_MSG_FAILED, "failed"));
829: return (ISC_R_UNEXPECTED);
830: }
831: #else /* ISC_PLATFORM_USETHREADS */
832: manager->refs = 1;
833: timermgr = manager;
834: #endif /* ISC_PLATFORM_USETHREADS */
835:
836: *managerp = manager;
837:
838: return (ISC_R_SUCCESS);
839: }
840:
841: void
842: isc_timermgr_poke(isc_timermgr_t *manager) {
843: #ifdef ISC_PLATFORM_USETHREADS
844: REQUIRE(VALID_MANAGER(manager));
845:
846: SIGNAL(&manager->wakeup);
847: #else
848: UNUSED(manager);
849: #endif
850: }
851:
852: void
853: isc_timermgr_destroy(isc_timermgr_t **managerp) {
854: isc_timermgr_t *manager;
855: isc_mem_t *mctx;
856:
857: /*
858: * Destroy a timer manager.
859: */
860:
861: REQUIRE(managerp != NULL);
862: manager = *managerp;
863: REQUIRE(VALID_MANAGER(manager));
864:
865: LOCK(&manager->lock);
866:
867: #ifndef ISC_PLATFORM_USETHREADS
868: if (manager->refs > 1) {
869: manager->refs--;
870: UNLOCK(&manager->lock);
871: *managerp = NULL;
872: return;
873: }
874:
875: isc__timermgr_dispatch();
876: #endif /* ISC_PLATFORM_USETHREADS */
877:
878: REQUIRE(EMPTY(manager->timers));
879: manager->done = ISC_TRUE;
880:
881: #ifdef ISC_PLATFORM_USETHREADS
882: XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
883: ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
884: SIGNAL(&manager->wakeup);
885: #endif /* ISC_PLATFORM_USETHREADS */
886:
887: UNLOCK(&manager->lock);
888:
889: #ifdef ISC_PLATFORM_USETHREADS
890: /*
891: * Wait for thread to exit.
892: */
893: if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
894: UNEXPECTED_ERROR(__FILE__, __LINE__,
895: "isc_thread_join() %s",
896: isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
897: ISC_MSG_FAILED, "failed"));
898: #endif /* ISC_PLATFORM_USETHREADS */
899:
900: /*
901: * Clean up.
902: */
903: #ifdef ISC_PLATFORM_USETHREADS
904: (void)isc_condition_destroy(&manager->wakeup);
905: #endif /* ISC_PLATFORM_USETHREADS */
906: DESTROYLOCK(&manager->lock);
907: isc_heap_destroy(&manager->heap);
908: manager->magic = 0;
909: mctx = manager->mctx;
910: isc_mem_put(mctx, manager, sizeof(*manager));
911: isc_mem_detach(&mctx);
912:
913: *managerp = NULL;
914: }
915:
916: #ifndef ISC_PLATFORM_USETHREADS
917: isc_result_t
918: isc__timermgr_nextevent(isc_time_t *when) {
919: if (timermgr == NULL || timermgr->nscheduled == 0)
920: return (ISC_R_NOTFOUND);
921: *when = timermgr->due;
922: return (ISC_R_SUCCESS);
923: }
924:
925: void
926: isc__timermgr_dispatch(void) {
927: isc_time_t now;
928: if (timermgr == NULL)
929: return;
930: TIME_NOW(&now);
931: dispatch(timermgr, &now);
932: }
933: #endif /* ISC_PLATFORM_USETHREADS */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>