2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1995-1999 by Internet Software Consortium
5 * Permission to use, copy, modify, and 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.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * $Id: ev_timers.c,v 1.6 2005/04/27 04:56:36 sra Exp $
20 /* ev_timers.c - implement timers for the eventlib
21 * vix 09sep95 [initial]
26 #include "port_before.h"
28 #include "fd_setsize.h"
34 #include <isc/assertions.h>
36 #include "isc/eventlib.h"
37 #include "eventlib_p.h"
39 #include "port_after.h"
43 #define MILLION 1000000
44 #define BILLION 1000000000
48 static int __evOptMonoTime
;
50 static int due_sooner(void *, void *);
51 static void set_index(void *, int);
52 static void free_timer(void *, void *);
53 static void print_timer(void *, void *);
54 static void idle_timeout(evContext
, void *, struct timespec
, struct timespec
);
61 struct timespec lastTouched
;
62 struct timespec max_idle
;
69 evConsTime(time_t sec
, long nsec
) {
78 evAddTime(struct timespec addend1
, struct timespec addend2
) {
81 x
.tv_sec
= addend1
.tv_sec
+ addend2
.tv_sec
;
82 x
.tv_nsec
= addend1
.tv_nsec
+ addend2
.tv_nsec
;
83 if (x
.tv_nsec
>= BILLION
) {
91 evSubTime(struct timespec minuend
, struct timespec subtrahend
) {
94 x
.tv_sec
= minuend
.tv_sec
- subtrahend
.tv_sec
;
95 if (minuend
.tv_nsec
>= subtrahend
.tv_nsec
)
96 x
.tv_nsec
= minuend
.tv_nsec
- subtrahend
.tv_nsec
;
98 x
.tv_nsec
= BILLION
- subtrahend
.tv_nsec
+ minuend
.tv_nsec
;
105 evCmpTime(struct timespec a
, struct timespec b
) {
106 long x
= a
.tv_sec
- b
.tv_sec
;
109 x
= a
.tv_nsec
- b
.tv_nsec
;
110 return (x
< 0L ? (-1) : x
> 0L ? (1) : (0));
116 #ifdef CLOCK_REALTIME
117 struct timespec tsnow
;
118 int m
= CLOCK_REALTIME
;
120 #ifdef CLOCK_MONOTONIC
124 if (clock_gettime(m
, &tsnow
) == 0)
127 if (gettimeofday(&now
, NULL
) < 0)
128 return (evConsTime(0, 0));
129 return (evTimeSpec(now
));
135 #ifdef CLOCK_REALTIME
136 struct timespec tsnow
;
137 if (clock_gettime(CLOCK_REALTIME
, &tsnow
) == 0)
140 if (gettimeofday(&now
, NULL
) < 0)
141 return (evConsTime(0, 0));
142 return (evTimeSpec(now
));
147 evLastEventTime(evContext opaqueCtx
) {
148 evContext_p
*ctx
= opaqueCtx
.opaque
;
150 return (ctx
->lastEventTime
);
155 evTimeSpec(struct timeval tv
) {
158 ts
.tv_sec
= tv
.tv_sec
;
159 ts
.tv_nsec
= tv
.tv_usec
* 1000;
162 #if !defined(USE_KQUEUE) || !defined(_LIBC)
164 evTimeVal(struct timespec ts
) {
167 tv
.tv_sec
= ts
.tv_sec
;
168 tv
.tv_usec
= ts
.tv_nsec
/ 1000;
175 evSetTimer(evContext opaqueCtx
,
179 struct timespec inter
,
182 evContext_p
*ctx
= opaqueCtx
.opaque
;
186 "evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
188 (long)due
.tv_sec
, due
.tv_nsec
,
189 (long)inter
.tv_sec
, inter
.tv_nsec
);
193 * tv_sec and tv_nsec are unsigned.
195 if (due
.tv_nsec
>= BILLION
)
198 if (inter
.tv_nsec
>= BILLION
)
201 if (due
.tv_sec
< 0 || due
.tv_nsec
< 0 || due
.tv_nsec
>= BILLION
)
204 if (inter
.tv_sec
< 0 || inter
.tv_nsec
< 0 || inter
.tv_nsec
>= BILLION
)
208 /* due={0,0} is a magic cookie meaning "now." */
209 if (due
.tv_sec
== (time_t)0 && due
.tv_nsec
== 0L)
212 /* Allocate and fill. */
219 if (heap_insert(ctx
->timers
, id
) < 0)
222 /* Remember the ID if the caller provided us a place for it. */
224 opaqueID
->opaque
= id
;
226 if (ctx
->debug
> 7) {
227 evPrintf(ctx
, 7, "timers after evSetTimer:\n");
228 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
235 evClearTimer(evContext opaqueCtx
, evTimerID id
) {
236 evContext_p
*ctx
= opaqueCtx
.opaque
;
237 evTimer
*del
= id
.opaque
;
239 if (ctx
->cur
!= NULL
&&
240 ctx
->cur
->type
== Timer
&&
241 ctx
->cur
->u
.timer
.this == del
) {
242 evPrintf(ctx
, 8, "deferring delete of timer (executing)\n");
244 * Setting the interval to zero ensures that evDrop() will
245 * clean up the timer.
247 del
->inter
= evConsTime(0, 0);
251 if (heap_element(ctx
->timers
, del
->index
) != del
)
254 if (heap_delete(ctx
->timers
, del
->index
) < 0)
258 if (ctx
->debug
> 7) {
259 evPrintf(ctx
, 7, "timers after evClearTimer:\n");
260 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
267 evConfigTimer(evContext opaqueCtx
,
272 evContext_p
*ctx
= opaqueCtx
.opaque
;
273 evTimer
*timer
= id
.opaque
;
278 if (heap_element(ctx
->timers
, timer
->index
) != timer
)
281 if (strcmp(param
, "rate") == 0)
282 timer
->mode
|= EV_TMR_RATE
;
283 else if (strcmp(param
, "interval") == 0)
284 timer
->mode
&= ~EV_TMR_RATE
;
292 evResetTimer(evContext opaqueCtx
,
297 struct timespec inter
299 evContext_p
*ctx
= opaqueCtx
.opaque
;
300 evTimer
*timer
= id
.opaque
;
301 struct timespec old_due
;
304 if (heap_element(ctx
->timers
, timer
->index
) != timer
)
309 * tv_sec and tv_nsec are unsigned.
311 if (due
.tv_nsec
>= BILLION
)
314 if (inter
.tv_nsec
>= BILLION
)
317 if (due
.tv_sec
< 0 || due
.tv_nsec
< 0 || due
.tv_nsec
>= BILLION
)
320 if (inter
.tv_sec
< 0 || inter
.tv_nsec
< 0 || inter
.tv_nsec
>= BILLION
)
324 old_due
= timer
->due
;
329 timer
->inter
= inter
;
331 switch (evCmpTime(due
, old_due
)) {
333 result
= heap_increased(ctx
->timers
, timer
->index
);
339 result
= heap_decreased(ctx
->timers
, timer
->index
);
343 if (ctx
->debug
> 7) {
344 evPrintf(ctx
, 7, "timers after evResetTimer:\n");
345 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
352 evSetIdleTimer(evContext opaqueCtx
,
355 struct timespec max_idle
,
358 evContext_p
*ctx
= opaqueCtx
.opaque
;
361 /* Allocate and fill. */
365 tt
->lastTouched
= ctx
->lastEventTime
;
366 tt
->max_idle
= max_idle
;
368 if (evSetTimer(opaqueCtx
, idle_timeout
, tt
,
369 evAddTime(ctx
->lastEventTime
, max_idle
),
370 max_idle
, opaqueID
) < 0) {
375 tt
->timer
= opaqueID
->opaque
;
381 evClearIdleTimer(evContext opaqueCtx
, evTimerID id
) {
382 evTimer
*del
= id
.opaque
;
383 idle_timer
*tt
= del
->uap
;
386 return (evClearTimer(opaqueCtx
, id
));
390 evResetIdleTimer(evContext opaqueCtx
,
394 struct timespec max_idle
396 evContext_p
*ctx
= opaqueCtx
.opaque
;
397 evTimer
*timer
= opaqueID
.opaque
;
398 idle_timer
*tt
= timer
->uap
;
402 tt
->lastTouched
= ctx
->lastEventTime
;
403 tt
->max_idle
= max_idle
;
405 return (evResetTimer(opaqueCtx
, opaqueID
, idle_timeout
, tt
,
406 evAddTime(ctx
->lastEventTime
, max_idle
),
411 evTouchIdleTimer(evContext opaqueCtx
, evTimerID id
) {
412 evContext_p
*ctx
= opaqueCtx
.opaque
;
413 evTimer
*t
= id
.opaque
;
414 idle_timer
*tt
= t
->uap
;
416 tt
->lastTouched
= ctx
->lastEventTime
;
421 /* Public to the rest of eventlib. */
424 evCreateTimers(const evContext_p
*ctx
) {
428 return (heap_new(due_sooner
, set_index
, 2048));
432 evDestroyTimers(const evContext_p
*ctx
) {
433 (void) heap_for_each(ctx
->timers
, free_timer
, NULL
);
434 (void) heap_free(ctx
->timers
);
440 due_sooner(void *a
, void *b
) {
441 evTimer
*a_timer
, *b_timer
;
445 return (evCmpTime(a_timer
->due
, b_timer
->due
) < 0);
449 set_index(void *what
, int index
) {
453 timer
->index
= index
;
457 free_timer(void *what
, void *uap
) {
466 print_timer(void *what
, void *uap
) {
468 evContext_p
*ctx
= uap
;
472 " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
474 (long)cur
->due
.tv_sec
, cur
->due
.tv_nsec
,
475 (long)cur
->inter
.tv_sec
, cur
->inter
.tv_nsec
);
479 idle_timeout(evContext opaqueCtx
,
482 struct timespec inter
484 evContext_p
*ctx
= opaqueCtx
.opaque
;
485 idle_timer
*this = uap
;
486 struct timespec idle
;
491 idle
= evSubTime(ctx
->lastEventTime
, this->lastTouched
);
492 if (evCmpTime(idle
, this->max_idle
) >= 0) {
493 (this->func
)(opaqueCtx
, this->uap
, this->timer
->due
,
496 * Setting the interval to zero will cause the timer to
497 * be cleaned up in evDrop().
499 this->timer
->inter
= evConsTime(0, 0);
502 /* evDrop() will reschedule the timer. */
503 this->timer
->inter
= evSubTime(this->max_idle
, idle
);