1 /* $NetBSD: ev_timers.c,v 1.11 2012/03/21 00:34:54 christos Exp $ */
4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1995-1999 by Internet Software Consortium
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 /* ev_timers.c - implement timers for the eventlib
21 * vix 09sep95 [initial]
24 #include <sys/cdefs.h>
25 #if !defined(LINT) && !defined(CODECENTER) && !defined(lint)
27 static const char rcsid
[] = "Id: ev_timers.c,v 1.6 2005/04/27 04:56:36 sra Exp";
29 __RCSID("$NetBSD: ev_timers.c,v 1.11 2012/03/21 00:34:54 christos Exp $");
35 #include "port_before.h"
36 #include "fd_setsize.h"
40 #include <isc/assertions.h>
41 #include <isc/eventlib.h>
42 #include "eventlib_p.h"
44 #include "port_after.h"
48 #define MILLION 1000000
49 #define BILLION 1000000000
54 static int due_sooner(void *, void *);
55 static void set_index(void *, int);
56 static void free_timer(void *, void *);
57 static void print_timer(void *, void *);
58 static void idle_timeout(evContext
, void *, struct timespec
, struct timespec
);
65 struct timespec lastTouched
;
66 struct timespec max_idle
;
74 evConsTime(time_t sec
, long nsec
) {
83 evAddTime(struct timespec addend1
, struct timespec addend2
) {
86 x
.tv_sec
= addend1
.tv_sec
+ addend2
.tv_sec
;
87 x
.tv_nsec
= addend1
.tv_nsec
+ addend2
.tv_nsec
;
88 if (x
.tv_nsec
>= BILLION
) {
96 evSubTime(struct timespec minuend
, struct timespec subtrahend
) {
99 x
.tv_sec
= minuend
.tv_sec
- subtrahend
.tv_sec
;
100 if (minuend
.tv_nsec
>= subtrahend
.tv_nsec
)
101 x
.tv_nsec
= minuend
.tv_nsec
- subtrahend
.tv_nsec
;
103 x
.tv_nsec
= BILLION
- subtrahend
.tv_nsec
+ minuend
.tv_nsec
;
110 evCmpTime(struct timespec a
, struct timespec b
) {
111 #define SGN(x) ((x) < 0 ? (-1) : (x) > 0 ? (1) : (0));
112 time_t s
= a
.tv_sec
- b
.tv_sec
;
118 n
= a
.tv_nsec
- b
.tv_nsec
;
126 #ifdef CLOCK_REALTIME
127 struct timespec tsnow
;
128 int m
= CLOCK_REALTIME
;
130 #ifdef CLOCK_MONOTONIC
136 if (clock_gettime(m
, &tsnow
) == 0)
139 if (gettimeofday(&now
, NULL
) < 0)
140 return (evConsTime((time_t)0, 0L));
141 return (evTimeSpec(now
));
147 #ifdef CLOCK_REALTIME
148 struct timespec tsnow
;
149 if (clock_gettime(CLOCK_REALTIME
, &tsnow
) == 0)
152 if (gettimeofday(&now
, NULL
) < 0)
153 return (evConsTime((time_t)0, 0L));
154 return (evTimeSpec(now
));
159 evLastEventTime(evContext opaqueCtx
) {
160 evContext_p
*ctx
= opaqueCtx
.opaque
;
162 return (ctx
->lastEventTime
);
167 evTimeSpec(struct timeval tv
) {
170 ts
.tv_sec
= tv
.tv_sec
;
171 ts
.tv_nsec
= tv
.tv_usec
* 1000;
176 evTimeVal(struct timespec ts
) {
179 tv
.tv_sec
= ts
.tv_sec
;
180 tv
.tv_usec
= (suseconds_t
)(ts
.tv_nsec
/ 1000);
186 evSetTimer(evContext opaqueCtx
,
190 struct timespec inter
,
193 evContext_p
*ctx
= opaqueCtx
.opaque
;
197 "evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
199 (long)due
.tv_sec
, due
.tv_nsec
,
200 (long)inter
.tv_sec
, inter
.tv_nsec
);
204 * tv_sec and tv_nsec are unsigned.
206 if (due
.tv_nsec
>= BILLION
)
209 if (inter
.tv_nsec
>= BILLION
)
212 if (due
.tv_sec
< 0 || due
.tv_nsec
< 0 || due
.tv_nsec
>= BILLION
)
215 if (inter
.tv_sec
< 0 || inter
.tv_nsec
< 0 || inter
.tv_nsec
>= BILLION
)
219 /* due={0,0} is a magic cookie meaning "now." */
220 if (due
.tv_sec
== (time_t)0 && due
.tv_nsec
== 0L)
223 /* Allocate and fill. */
230 if (heap_insert(ctx
->timers
, id
) < 0)
233 /* Remember the ID if the caller provided us a place for it. */
235 opaqueID
->opaque
= id
;
237 if (ctx
->debug
> 7) {
238 evPrintf(ctx
, 7, "timers after evSetTimer:\n");
239 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
246 evClearTimer(evContext opaqueCtx
, evTimerID id
) {
247 evContext_p
*ctx
= opaqueCtx
.opaque
;
248 evTimer
*del
= id
.opaque
;
250 if (ctx
->cur
!= NULL
&&
251 ctx
->cur
->type
== Timer
&&
252 ctx
->cur
->u
.timer
.this == del
) {
253 evPrintf(ctx
, 8, "deferring delete of timer (executing)\n");
255 * Setting the interval to zero ensures that evDrop() will
256 * clean up the timer.
258 del
->inter
= evConsTime(0, 0);
262 if (heap_element(ctx
->timers
, del
->index
) != del
)
265 if (heap_delete(ctx
->timers
, del
->index
) < 0)
269 if (ctx
->debug
> 7) {
270 evPrintf(ctx
, 7, "timers after evClearTimer:\n");
271 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
278 evConfigTimer(evContext opaqueCtx
,
283 evContext_p
*ctx
= opaqueCtx
.opaque
;
284 evTimer
*timer
= id
.opaque
;
289 if (heap_element(ctx
->timers
, timer
->index
) != timer
)
292 if (strcmp(param
, "rate") == 0)
293 timer
->mode
|= EV_TMR_RATE
;
294 else if (strcmp(param
, "interval") == 0)
295 timer
->mode
&= ~EV_TMR_RATE
;
303 evResetTimer(evContext opaqueCtx
,
308 struct timespec inter
310 evContext_p
*ctx
= opaqueCtx
.opaque
;
311 evTimer
*timer
= id
.opaque
;
312 struct timespec old_due
;
315 if (heap_element(ctx
->timers
, timer
->index
) != timer
)
320 * tv_sec and tv_nsec are unsigned.
322 if (due
.tv_nsec
>= BILLION
)
325 if (inter
.tv_nsec
>= BILLION
)
328 if (due
.tv_sec
< 0 || due
.tv_nsec
< 0 || due
.tv_nsec
>= BILLION
)
331 if (inter
.tv_sec
< 0 || inter
.tv_nsec
< 0 || inter
.tv_nsec
>= BILLION
)
335 old_due
= timer
->due
;
340 timer
->inter
= inter
;
342 switch (evCmpTime(due
, old_due
)) {
344 result
= heap_increased(ctx
->timers
, timer
->index
);
350 result
= heap_decreased(ctx
->timers
, timer
->index
);
354 if (ctx
->debug
> 7) {
355 evPrintf(ctx
, 7, "timers after evResetTimer:\n");
356 (void) heap_for_each(ctx
->timers
, print_timer
, (void *)ctx
);
363 evSetIdleTimer(evContext opaqueCtx
,
366 struct timespec max_idle
,
369 evContext_p
*ctx
= opaqueCtx
.opaque
;
372 /* Allocate and fill. */
376 tt
->lastTouched
= ctx
->lastEventTime
;
377 tt
->max_idle
= max_idle
;
379 if (evSetTimer(opaqueCtx
, idle_timeout
, tt
,
380 evAddTime(ctx
->lastEventTime
, max_idle
),
381 max_idle
, opaqueID
) < 0) {
386 tt
->timer
= opaqueID
->opaque
;
392 evClearIdleTimer(evContext opaqueCtx
, evTimerID id
) {
393 evTimer
*del
= id
.opaque
;
394 idle_timer
*tt
= del
->uap
;
397 return (evClearTimer(opaqueCtx
, id
));
401 evResetIdleTimer(evContext opaqueCtx
,
405 struct timespec max_idle
407 evContext_p
*ctx
= opaqueCtx
.opaque
;
408 evTimer
*timer
= opaqueID
.opaque
;
409 idle_timer
*tt
= timer
->uap
;
413 tt
->lastTouched
= ctx
->lastEventTime
;
414 tt
->max_idle
= max_idle
;
416 return (evResetTimer(opaqueCtx
, opaqueID
, idle_timeout
, tt
,
417 evAddTime(ctx
->lastEventTime
, max_idle
),
422 evTouchIdleTimer(evContext opaqueCtx
, evTimerID id
) {
423 evContext_p
*ctx
= opaqueCtx
.opaque
;
424 evTimer
*t
= id
.opaque
;
425 idle_timer
*tt
= t
->uap
;
427 tt
->lastTouched
= ctx
->lastEventTime
;
432 /* Public to the rest of eventlib. */
435 evCreateTimers(const evContext_p
*ctx
) {
439 return (heap_new(due_sooner
, set_index
, 2048));
443 evDestroyTimers(const evContext_p
*ctx
) {
444 (void) heap_for_each(ctx
->timers
, free_timer
, NULL
);
445 (void) heap_free(ctx
->timers
);
451 due_sooner(void *a
, void *b
) {
452 evTimer
*a_timer
, *b_timer
;
456 return (evCmpTime(a_timer
->due
, b_timer
->due
) < 0);
460 set_index(void *what
, int idx
) {
468 free_timer(void *what
, void *uap
) {
477 print_timer(void *what
, void *uap
) {
479 evContext_p
*ctx
= uap
;
483 " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
485 (long)cur
->due
.tv_sec
, cur
->due
.tv_nsec
,
486 (long)cur
->inter
.tv_sec
, cur
->inter
.tv_nsec
);
490 idle_timeout(evContext opaqueCtx
,
493 struct timespec inter
495 evContext_p
*ctx
= opaqueCtx
.opaque
;
496 idle_timer
*this = uap
;
497 struct timespec idle
;
502 idle
= evSubTime(ctx
->lastEventTime
, this->lastTouched
);
503 if (evCmpTime(idle
, this->max_idle
) >= 0) {
504 (this->func
)(opaqueCtx
, this->uap
, this->timer
->due
,
507 * Setting the interval to zero will cause the timer to
508 * be cleaned up in evDrop().
510 this->timer
->inter
= evConsTime(0L, 0L);
513 /* evDrop() will reschedule the timer. */
514 this->timer
->inter
= evSubTime(this->max_idle
, idle
);