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