Home | History | Annotate | Download | only in isc
      1 /*	$NetBSD: ev_timers.c,v 1.2 2004/05/20 19:52:31 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
      5  * Copyright (c) 1995-1999 by Internet Software Consortium
      6  *
      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.
     10  *
     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.
     18  */
     19 
     20 /* ev_timers.c - implement timers for the eventlib
     21  * vix 09sep95 [initial]
     22  */
     23 
     24 #include <sys/cdefs.h>
     25 #if !defined(LINT) && !defined(CODECENTER) && !defined(lint)
     26 #ifdef notdef
     27 static const char rcsid[] = "Id: ev_timers.c,v 1.2.2.1.4.5 2004/03/17 02:39:13 marka Exp";
     28 #else
     29 __RCSID("$NetBSD: ev_timers.c,v 1.2 2004/05/20 19:52:31 christos Exp $");
     30 #endif
     31 #endif
     32 
     33 /* Import. */
     34 
     35 #include <errno.h>
     36 #include <time.h>
     37 
     38 #include <isc/assertions.h>
     39 #include <isc/eventlib.h>
     40 #include "eventlib_p.h"
     41 
     42 /* Constants. */
     43 
     44 #define	MILLION 1000000
     45 #define BILLION 1000000000
     46 
     47 /* Forward. */
     48 
     49 #ifndef _LIBC
     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);
     55 
     56 /* Private type. */
     57 
     58 typedef struct {
     59 	evTimerFunc	func;
     60 	void *		uap;
     61 	struct timespec	lastTouched;
     62 	struct timespec	max_idle;
     63 	evTimer *	timer;
     64 } idle_timer;
     65 #endif
     66 
     67 /* Public. */
     68 
     69 struct timespec
     70 evConsTime(time_t sec, long nsec) {
     71 	struct timespec x;
     72 
     73 	x.tv_sec = sec;
     74 	x.tv_nsec = nsec;
     75 	return (x);
     76 }
     77 
     78 struct timespec
     79 evAddTime(struct timespec addend1, struct timespec addend2) {
     80 	struct timespec x;
     81 
     82 	x.tv_sec = addend1.tv_sec + addend2.tv_sec;
     83 	x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
     84 	if (x.tv_nsec >= BILLION) {
     85 		x.tv_sec++;
     86 		x.tv_nsec -= BILLION;
     87 	}
     88 	return (x);
     89 }
     90 
     91 struct timespec
     92 evSubTime(struct timespec minuend, struct timespec subtrahend) {
     93 	struct timespec x;
     94 
     95 	x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
     96 	if (minuend.tv_nsec >= subtrahend.tv_nsec)
     97 		x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
     98 	else {
     99 		x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
    100 		x.tv_sec--;
    101 	}
    102 	return (x);
    103 }
    104 
    105 int
    106 evCmpTime(struct timespec a, struct timespec b) {
    107 	long x = a.tv_sec - b.tv_sec;
    108 
    109 	if (x == 0L)
    110 		x = a.tv_nsec - b.tv_nsec;
    111 	return (x < 0L ? (-1) : x > 0L ? (1) : (0));
    112 }
    113 
    114 struct timespec
    115 evNowTime() {
    116 	struct timeval now;
    117 #ifdef CLOCK_REALTIME
    118 	struct timespec tsnow;
    119 	int m = CLOCK_REALTIME;
    120 
    121 #ifdef CLOCK_MONOTONIC
    122 	if (__evOptMonoTime)
    123 		m = CLOCK_MONOTONIC;
    124 #endif
    125 	if (clock_gettime(m, &tsnow) == 0)
    126 		return (tsnow);
    127 #endif
    128 	if (gettimeofday(&now, NULL) < 0)
    129 		return (evConsTime(0L, 0L));
    130 	return (evTimeSpec(now));
    131 }
    132 
    133 struct timespec
    134 evUTCTime(void) {
    135 	struct timeval now;
    136 #ifdef CLOCK_REALTIME
    137 	struct timespec tsnow;
    138 	if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0)
    139 		return (tsnow);
    140 #endif
    141 	if (gettimeofday(&now, NULL) < 0)
    142 		return (evConsTime(0L, 0L));
    143 	return (evTimeSpec(now));
    144 }
    145 
    146 #ifndef _LIBC
    147 struct timespec
    148 evLastEventTime(evContext opaqueCtx) {
    149 	evContext_p *ctx = opaqueCtx.opaque;
    150 
    151 	return (ctx->lastEventTime);
    152 }
    153 #endif
    154 
    155 struct timespec
    156 evTimeSpec(struct timeval tv) {
    157 	struct timespec ts;
    158 
    159 	ts.tv_sec = tv.tv_sec;
    160 	ts.tv_nsec = tv.tv_usec * 1000;
    161 	return (ts);
    162 }
    163 
    164 struct timeval
    165 evTimeVal(struct timespec ts) {
    166 	struct timeval tv;
    167 
    168 	tv.tv_sec = ts.tv_sec;
    169 	tv.tv_usec = ts.tv_nsec / 1000;
    170 	return (tv);
    171 }
    172 
    173 #ifndef _LIBC
    174 int
    175 evSetTimer(evContext opaqueCtx,
    176 	   evTimerFunc func,
    177 	   void *uap,
    178 	   struct timespec due,
    179 	   struct timespec inter,
    180 	   evTimerID *opaqueID
    181 ) {
    182 	evContext_p *ctx = opaqueCtx.opaque;
    183 	evTimer *id;
    184 
    185 	printf("evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
    186 		 ctx, func, uap,
    187 		 (long)due.tv_sec, due.tv_nsec,
    188 		 (long)inter.tv_sec, inter.tv_nsec);
    189 
    190 #ifdef __hpux
    191 	/*
    192 	 * tv_sec and tv_nsec are unsigned.
    193 	 */
    194 	if (due.tv_nsec >= BILLION)
    195 		EV_ERR(EINVAL);
    196 
    197 	if (inter.tv_nsec >= BILLION)
    198 		EV_ERR(EINVAL);
    199 #else
    200 	if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
    201 		EV_ERR(EINVAL);
    202 
    203 	if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
    204 		EV_ERR(EINVAL);
    205 #endif
    206 
    207 	/* due={0,0} is a magic cookie meaning "now." */
    208 	if (due.tv_sec == (time_t)0 && due.tv_nsec == 0L)
    209 		due = evNowTime();
    210 
    211 	/* Allocate and fill. */
    212 	OKNEW(id);
    213 	id->func = func;
    214 	id->uap = uap;
    215 	id->due = due;
    216 	id->inter = inter;
    217 
    218 	if (heap_insert(ctx->timers, id) < 0)
    219 		return (-1);
    220 
    221 	/* Remember the ID if the caller provided us a place for it. */
    222 	if (opaqueID)
    223 		opaqueID->opaque = id;
    224 
    225 	if (ctx->debug > 7) {
    226 		printf("timers after evSetTimer:\n");
    227 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
    228 	}
    229 
    230 	return (0);
    231 }
    232 
    233 int
    234 evClearTimer(evContext opaqueCtx, evTimerID id) {
    235 	evContext_p *ctx = opaqueCtx.opaque;
    236 	evTimer *del = id.opaque;
    237 
    238 	if (ctx->cur != NULL &&
    239 	    ctx->cur->type == Timer &&
    240 	    ctx->cur->u.timer.this == del) {
    241 		printf("deferring delete of timer (executing)\n");
    242 		/*
    243 		 * Setting the interval to zero ensures that evDrop() will
    244 		 * clean up the timer.
    245 		 */
    246 		del->inter = evConsTime(0, 0);
    247 		return (0);
    248 	}
    249 
    250 	if (heap_element(ctx->timers, del->index) != del)
    251 		EV_ERR(ENOENT);
    252 
    253 	if (heap_delete(ctx->timers, del->index) < 0)
    254 		return (-1);
    255 	FREE(del);
    256 
    257 	if (ctx->debug > 7) {
    258 		printf("timers after evClearTimer:\n");
    259 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
    260 	}
    261 
    262 	return (0);
    263 }
    264 
    265 int
    266 evConfigTimer(evContext opaqueCtx,
    267 	     evTimerID id,
    268 	     const char *param,
    269 	     int value
    270 ) {
    271 	evContext_p *ctx = opaqueCtx.opaque;
    272 	evTimer *timer = id.opaque;
    273 	int result=0;
    274 
    275 	UNUSED(value);
    276 
    277 	if (heap_element(ctx->timers, timer->index) != timer)
    278 		EV_ERR(ENOENT);
    279 
    280 	if (strcmp(param, "rate") == 0)
    281 		timer->mode |= EV_TMR_RATE;
    282 	else if (strcmp(param, "interval") == 0)
    283 		timer->mode &= ~EV_TMR_RATE;
    284 	else
    285 		EV_ERR(EINVAL);
    286 
    287 	return (result);
    288 }
    289 
    290 int
    291 evResetTimer(evContext opaqueCtx,
    292 	     evTimerID id,
    293 	     evTimerFunc func,
    294 	     void *uap,
    295 	     struct timespec due,
    296 	     struct timespec inter
    297 ) {
    298 	evContext_p *ctx = opaqueCtx.opaque;
    299 	evTimer *timer = id.opaque;
    300 	struct timespec old_due;
    301 	int result=0;
    302 
    303 	if (heap_element(ctx->timers, timer->index) != timer)
    304 		EV_ERR(ENOENT);
    305 
    306 #ifdef __hpux
    307 	/*
    308 	 * tv_sec and tv_nsec are unsigned.
    309 	 */
    310 	if (due.tv_nsec >= BILLION)
    311 		EV_ERR(EINVAL);
    312 
    313 	if (inter.tv_nsec >= BILLION)
    314 		EV_ERR(EINVAL);
    315 #else
    316 	if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
    317 		EV_ERR(EINVAL);
    318 
    319 	if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
    320 		EV_ERR(EINVAL);
    321 #endif
    322 
    323 	old_due = timer->due;
    324 
    325 	timer->func = func;
    326 	timer->uap = uap;
    327 	timer->due = due;
    328 	timer->inter = inter;
    329 
    330 	switch (evCmpTime(due, old_due)) {
    331 	case -1:
    332 		result = heap_increased(ctx->timers, timer->index);
    333 		break;
    334 	case 0:
    335 		result = 0;
    336 		break;
    337 	case 1:
    338 		result = heap_decreased(ctx->timers, timer->index);
    339 		break;
    340 	}
    341 
    342 	if (ctx->debug > 7) {
    343 		printf("timers after evResetTimer:\n");
    344 		(void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
    345 	}
    346 
    347 	return (result);
    348 }
    349 
    350 int
    351 evSetIdleTimer(evContext opaqueCtx,
    352 		evTimerFunc func,
    353 		void *uap,
    354 		struct timespec max_idle,
    355 		evTimerID *opaqueID
    356 ) {
    357 	evContext_p *ctx = opaqueCtx.opaque;
    358 	idle_timer *tt;
    359 
    360 	/* Allocate and fill. */
    361 	OKNEW(tt);
    362 	tt->func = func;
    363 	tt->uap = uap;
    364 	tt->lastTouched = ctx->lastEventTime;
    365 	tt->max_idle = max_idle;
    366 
    367 	if (evSetTimer(opaqueCtx, idle_timeout, tt,
    368 		       evAddTime(ctx->lastEventTime, max_idle),
    369 		       max_idle, opaqueID) < 0) {
    370 		FREE(tt);
    371 		return (-1);
    372 	}
    373 
    374 	tt->timer = opaqueID->opaque;
    375 
    376 	return (0);
    377 }
    378 
    379 int
    380 evClearIdleTimer(evContext opaqueCtx, evTimerID id) {
    381 	evTimer *del = id.opaque;
    382 	idle_timer *tt = del->uap;
    383 
    384 	FREE(tt);
    385 	return (evClearTimer(opaqueCtx, id));
    386 }
    387 
    388 int
    389 evResetIdleTimer(evContext opaqueCtx,
    390 		 evTimerID opaqueID,
    391 		 evTimerFunc func,
    392 		 void *uap,
    393 		 struct timespec max_idle
    394 ) {
    395 	evContext_p *ctx = opaqueCtx.opaque;
    396 	evTimer *timer = opaqueID.opaque;
    397 	idle_timer *tt = timer->uap;
    398 
    399 	tt->func = func;
    400 	tt->uap = uap;
    401 	tt->lastTouched = ctx->lastEventTime;
    402 	tt->max_idle = max_idle;
    403 
    404 	return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt,
    405 			     evAddTime(ctx->lastEventTime, max_idle),
    406 			     max_idle));
    407 }
    408 
    409 int
    410 evTouchIdleTimer(evContext opaqueCtx, evTimerID id) {
    411 	evContext_p *ctx = opaqueCtx.opaque;
    412 	evTimer *t = id.opaque;
    413 	idle_timer *tt = t->uap;
    414 
    415 	tt->lastTouched = ctx->lastEventTime;
    416 
    417 	return (0);
    418 }
    419 
    420 /* Public to the rest of eventlib. */
    421 
    422 heap_context
    423 evCreateTimers(const evContext_p *ctx) {
    424 
    425 	UNUSED(ctx);
    426 
    427 	return (heap_new(due_sooner, set_index, 2048));
    428 }
    429 
    430 void
    431 evDestroyTimers(const evContext_p *ctx) {
    432 	(void) heap_for_each(ctx->timers, free_timer, NULL);
    433 	(void) heap_free(ctx->timers);
    434 }
    435 
    436 /* Private. */
    437 
    438 static int
    439 due_sooner(void *a, void *b) {
    440 	evTimer *a_timer, *b_timer;
    441 
    442 	a_timer = a;
    443 	b_timer = b;
    444 	return (evCmpTime(a_timer->due, b_timer->due) < 0);
    445 }
    446 
    447 static void
    448 set_index(void *what, int idx) {
    449 	evTimer *timer;
    450 
    451 	timer = what;
    452 	timer->index = idx;
    453 }
    454 
    455 static void
    456 free_timer(void *what, void *uap) {
    457 	evTimer *t = what;
    458 
    459 	UNUSED(uap);
    460 
    461 	FREE(t);
    462 }
    463 
    464 static void
    465 print_timer(void *what, void *uap) {
    466 	evTimer *cur = what;
    467 	evContext_p *ctx = uap;
    468 
    469 	cur = what;
    470 	evPrintf(ctx, 7,
    471 	    "  func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
    472 		 cur->func, cur->uap,
    473 		 (long)cur->due.tv_sec, cur->due.tv_nsec,
    474 		 (long)cur->inter.tv_sec, cur->inter.tv_nsec);
    475 }
    476 
    477 static void
    478 idle_timeout(evContext opaqueCtx,
    479 	     void *uap,
    480 	     struct timespec due,
    481 	     struct timespec inter
    482 ) {
    483 	evContext_p *ctx = opaqueCtx.opaque;
    484 	idle_timer *this = uap;
    485 	struct timespec idle;
    486 
    487 	UNUSED(due);
    488 	UNUSED(inter);
    489 
    490 	idle = evSubTime(ctx->lastEventTime, this->lastTouched);
    491 	if (evCmpTime(idle, this->max_idle) >= 0) {
    492 		(this->func)(opaqueCtx, this->uap, this->timer->due,
    493 			     this->max_idle);
    494 		/*
    495 		 * Setting the interval to zero will cause the timer to
    496 		 * be cleaned up in evDrop().
    497 		 */
    498 		this->timer->inter = evConsTime(0L, 0L);
    499 		FREE(this);
    500 	} else {
    501 		/* evDrop() will reschedule the timer. */
    502 		this->timer->inter = evSubTime(this->max_idle, idle);
    503 	}
    504 }
    505 #endif
    506