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