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