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