1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /* $Id: timer.c,v 1.18 2020/02/16 21:11:02 florian Exp $ */ 18 19 /*! \file */ 20 21 22 #include <stdlib.h> 23 #include <isc/heap.h> 24 #include <isc/magic.h> 25 #include <isc/task.h> 26 #include <isc/time.h> 27 #include <isc/timer.h> 28 #include <isc/util.h> 29 30 #include "timer_p.h" 31 32 #define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R') 33 #define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC) 34 35 typedef struct isc__timer isc__timer_t; 36 typedef struct isc__timermgr isc__timermgr_t; 37 38 struct isc__timer { 39 /*! Not locked. */ 40 isc_timer_t common; 41 isc__timermgr_t * manager; 42 /*! Locked by timer lock. */ 43 unsigned int references; 44 struct timespec idle; 45 /*! Locked by manager lock. */ 46 struct timespec interval; 47 isc_task_t * task; 48 isc_taskaction_t action; 49 void * arg; 50 unsigned int index; 51 struct timespec due; 52 LINK(isc__timer_t) link; 53 }; 54 55 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M') 56 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC) 57 58 struct isc__timermgr { 59 /* Not locked. */ 60 isc_timermgr_t common; 61 /* Locked by manager lock. */ 62 isc_boolean_t done; 63 LIST(isc__timer_t) timers; 64 unsigned int nscheduled; 65 struct timespec due; 66 unsigned int refs; 67 isc_heap_t * heap; 68 }; 69 70 /*% 71 * The following are intended for internal use (indicated by "isc__" 72 * prefix) but are not declared as static, allowing direct access from 73 * unit tests etc. 74 */ 75 76 isc_result_t 77 isc__timer_create(isc_timermgr_t *manager, const struct timespec *interval, 78 isc_task_t *task, isc_taskaction_t action, void *arg, 79 isc_timer_t **timerp); 80 isc_result_t 81 isc__timer_reset(isc_timer_t *timer, const struct timespec *interval, 82 isc_boolean_t purge); 83 void 84 isc__timer_touch(isc_timer_t *timer); 85 void 86 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp); 87 void 88 isc__timer_detach(isc_timer_t **timerp); 89 isc_result_t 90 isc__timermgr_create(isc_timermgr_t **managerp); 91 void 92 isc__timermgr_destroy(isc_timermgr_t **managerp); 93 94 /*! 95 * If the manager is supposed to be shared, there can be only one. 96 */ 97 static isc__timermgr_t *timermgr = NULL; 98 99 static inline isc_result_t 100 schedule(isc__timer_t *timer, struct timespec *now, isc_boolean_t signal_ok) { 101 isc_result_t result; 102 isc__timermgr_t *manager; 103 struct timespec due; 104 105 /*! 106 * Note: the caller must ensure locking. 107 */ 108 109 UNUSED(signal_ok); 110 111 manager = timer->manager; 112 113 /* 114 * Compute the new due time. 115 */ 116 due = timer->idle; 117 118 /* 119 * Schedule the timer. 120 */ 121 122 if (timer->index > 0) { 123 /* 124 * Already scheduled. 125 */ 126 if (timespeccmp(&due, &timer->due, <)) 127 isc_heap_increased(manager->heap, timer->index); 128 else if (timespeccmp(&due, &timer->due, >)) 129 isc_heap_decreased(manager->heap, timer->index); 130 131 timer->due = due; 132 } else { 133 timer->due = due; 134 result = isc_heap_insert(manager->heap, timer); 135 if (result != ISC_R_SUCCESS) { 136 INSIST(result == ISC_R_NOMEMORY); 137 return (ISC_R_NOMEMORY); 138 } 139 manager->nscheduled++; 140 } 141 142 /* 143 * If this timer is at the head of the queue, we need to ensure 144 * that we won't miss it if it has a more recent due time than 145 * the current "next" timer. We do this either by waking up the 146 * run thread, or explicitly setting the value in the manager. 147 */ 148 if (timer->index == 1 && timespeccmp(&timer->due, &manager->due, <)) 149 manager->due = timer->due; 150 151 return (ISC_R_SUCCESS); 152 } 153 154 static inline void 155 deschedule(isc__timer_t *timer) { 156 isc__timermgr_t *manager; 157 158 /* 159 * The caller must ensure locking. 160 */ 161 162 manager = timer->manager; 163 if (timer->index > 0) { 164 isc_heap_delete(manager->heap, timer->index); 165 timer->index = 0; 166 INSIST(manager->nscheduled > 0); 167 manager->nscheduled--; 168 } 169 } 170 171 static void 172 destroy(isc__timer_t *timer) { 173 isc__timermgr_t *manager = timer->manager; 174 175 /* 176 * The caller must ensure it is safe to destroy the timer. 177 */ 178 179 (void)isc_task_purgerange(timer->task, 180 timer, 181 ISC_TIMEREVENT_FIRSTEVENT, 182 ISC_TIMEREVENT_LASTEVENT, 183 NULL); 184 deschedule(timer); 185 UNLINK(manager->timers, timer, link); 186 187 isc_task_detach(&timer->task); 188 timer->common.impmagic = 0; 189 timer->common.magic = 0; 190 free(timer); 191 } 192 193 isc_result_t 194 isc__timer_create(isc_timermgr_t *manager0, const struct timespec *interval, 195 isc_task_t *task, isc_taskaction_t action, void *arg, 196 isc_timer_t **timerp) 197 { 198 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 199 isc__timer_t *timer; 200 isc_result_t result; 201 struct timespec now; 202 203 /* 204 * Create a new 'type' timer managed by 'manager'. The timers 205 * parameters are specified by 'interval'. Events 206 * will be posted to 'task' and when dispatched 'action' will be 207 * called with 'arg' as the arg value. The new timer is returned 208 * in 'timerp'. 209 */ 210 211 REQUIRE(VALID_MANAGER(manager)); 212 REQUIRE(task != NULL); 213 REQUIRE(action != NULL); 214 REQUIRE(interval != NULL); 215 REQUIRE(timespecisset(interval)); 216 REQUIRE(timerp != NULL && *timerp == NULL); 217 218 /* 219 * Get current time. 220 */ 221 clock_gettime(CLOCK_REALTIME, &now); 222 223 timer = malloc(sizeof(*timer)); 224 if (timer == NULL) 225 return (ISC_R_NOMEMORY); 226 227 timer->manager = manager; 228 timer->references = 1; 229 230 if (timespecisset(interval)) 231 timespecadd(&now, interval, &timer->idle); 232 233 timer->interval = *interval; 234 timer->task = NULL; 235 isc_task_attach(task, &timer->task); 236 timer->action = action; 237 /* 238 * Removing the const attribute from "arg" is the best of two 239 * evils here. If the timer->arg member is made const, then 240 * it affects a great many recipients of the timer event 241 * which did not pass in an "arg" that was truly const. 242 * Changing isc_timer_create() to not have "arg" prototyped as const, 243 * though, can cause compilers warnings for calls that *do* 244 * have a truly const arg. The caller will have to carefully 245 * keep track of whether arg started as a true const. 246 */ 247 DE_CONST(arg, timer->arg); 248 timer->index = 0; 249 ISC_LINK_INIT(timer, link); 250 timer->common.impmagic = TIMER_MAGIC; 251 timer->common.magic = ISCAPI_TIMER_MAGIC; 252 253 result = schedule(timer, &now, ISC_TRUE); 254 if (result == ISC_R_SUCCESS) 255 APPEND(manager->timers, timer, link); 256 257 if (result != ISC_R_SUCCESS) { 258 timer->common.impmagic = 0; 259 timer->common.magic = 0; 260 isc_task_detach(&timer->task); 261 free(timer); 262 return (result); 263 } 264 265 *timerp = (isc_timer_t *)timer; 266 267 return (ISC_R_SUCCESS); 268 } 269 270 isc_result_t 271 isc__timer_reset(isc_timer_t *timer0, const struct timespec *interval, 272 isc_boolean_t purge) 273 { 274 isc__timer_t *timer = (isc__timer_t *)timer0; 275 struct timespec now; 276 isc__timermgr_t *manager; 277 isc_result_t result; 278 279 /* 280 * Change the timer's type, expires, and interval values to the given 281 * values. If 'purge' is ISC_TRUE, any pending events from this timer 282 * are purged from its task's event queue. 283 */ 284 285 REQUIRE(VALID_TIMER(timer)); 286 manager = timer->manager; 287 REQUIRE(VALID_MANAGER(manager)); 288 REQUIRE(interval != NULL); 289 REQUIRE(timespecisset(interval)); 290 291 /* 292 * Get current time. 293 */ 294 clock_gettime(CLOCK_REALTIME, &now); 295 296 if (purge) 297 (void)isc_task_purgerange(timer->task, 298 timer, 299 ISC_TIMEREVENT_FIRSTEVENT, 300 ISC_TIMEREVENT_LASTEVENT, 301 NULL); 302 timer->interval = *interval; 303 if (timespecisset(interval)) { 304 timespecadd(&now, interval, &timer->idle); 305 } else { 306 timespecclear(&timer->idle); 307 } 308 309 result = schedule(timer, &now, ISC_TRUE); 310 311 return (result); 312 } 313 314 void 315 isc__timer_touch(isc_timer_t *timer0) { 316 isc__timer_t *timer = (isc__timer_t *)timer0; 317 struct timespec now; 318 319 /* 320 * Set the last-touched time of 'timer' to the current time. 321 */ 322 323 REQUIRE(VALID_TIMER(timer)); 324 325 clock_gettime(CLOCK_REALTIME, &now); 326 timespecadd(&now, &timer->interval, &timer->idle); 327 } 328 329 void 330 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) { 331 isc__timer_t *timer = (isc__timer_t *)timer0; 332 333 /* 334 * Attach *timerp to timer. 335 */ 336 337 REQUIRE(VALID_TIMER(timer)); 338 REQUIRE(timerp != NULL && *timerp == NULL); 339 340 timer->references++; 341 342 *timerp = (isc_timer_t *)timer; 343 } 344 345 void 346 isc__timer_detach(isc_timer_t **timerp) { 347 isc__timer_t *timer; 348 isc_boolean_t free_timer = ISC_FALSE; 349 350 /* 351 * Detach *timerp from its timer. 352 */ 353 354 REQUIRE(timerp != NULL); 355 timer = (isc__timer_t *)*timerp; 356 REQUIRE(VALID_TIMER(timer)); 357 358 REQUIRE(timer->references > 0); 359 timer->references--; 360 if (timer->references == 0) 361 free_timer = ISC_TRUE; 362 363 if (free_timer) 364 destroy(timer); 365 366 *timerp = NULL; 367 } 368 369 static void 370 dispatch(isc__timermgr_t *manager, struct timespec *now) { 371 isc_boolean_t done = ISC_FALSE, post_event, need_schedule; 372 isc_timerevent_t *event; 373 isc_eventtype_t type = 0; 374 isc__timer_t *timer; 375 isc_result_t result; 376 isc_boolean_t idle; 377 378 /*! 379 * The caller must be holding the manager lock. 380 */ 381 382 while (manager->nscheduled > 0 && !done) { 383 timer = isc_heap_element(manager->heap, 1); 384 INSIST(timer != NULL); 385 if (timespeccmp(now, &timer->due, >=)) { 386 idle = ISC_FALSE; 387 388 if (timespecisset(&timer->idle) && timespeccmp(now, 389 &timer->idle, >=)) { 390 idle = ISC_TRUE; 391 } 392 if (idle) { 393 type = ISC_TIMEREVENT_IDLE; 394 post_event = ISC_TRUE; 395 need_schedule = ISC_FALSE; 396 } else { 397 /* 398 * Idle timer has been touched; 399 * reschedule. 400 */ 401 post_event = ISC_FALSE; 402 need_schedule = ISC_TRUE; 403 } 404 405 if (post_event) { 406 /* 407 * XXX We could preallocate this event. 408 */ 409 event = (isc_timerevent_t *)isc_event_allocate( 410 timer, 411 type, 412 timer->action, 413 timer->arg, 414 sizeof(*event)); 415 416 if (event != NULL) { 417 event->due = timer->due; 418 isc_task_send(timer->task, 419 ISC_EVENT_PTR(&event)); 420 } else 421 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", 422 "couldn't allocate event"); 423 } 424 425 timer->index = 0; 426 isc_heap_delete(manager->heap, 1); 427 manager->nscheduled--; 428 429 if (need_schedule) { 430 result = schedule(timer, now, ISC_FALSE); 431 if (result != ISC_R_SUCCESS) 432 UNEXPECTED_ERROR(__FILE__, __LINE__, 433 "%s: %u", 434 "couldn't schedule timer", 435 result); 436 } 437 } else { 438 manager->due = timer->due; 439 done = ISC_TRUE; 440 } 441 } 442 } 443 444 static isc_boolean_t 445 sooner(void *v1, void *v2) { 446 isc__timer_t *t1, *t2; 447 448 t1 = v1; 449 t2 = v2; 450 REQUIRE(VALID_TIMER(t1)); 451 REQUIRE(VALID_TIMER(t2)); 452 453 if (timespeccmp(&t1->due, &t2->due, <)) 454 return (ISC_TRUE); 455 return (ISC_FALSE); 456 } 457 458 static void 459 set_index(void *what, unsigned int index) { 460 isc__timer_t *timer; 461 462 timer = what; 463 REQUIRE(VALID_TIMER(timer)); 464 465 timer->index = index; 466 } 467 468 isc_result_t 469 isc__timermgr_create(isc_timermgr_t **managerp) { 470 isc__timermgr_t *manager; 471 isc_result_t result; 472 473 /* 474 * Create a timer manager. 475 */ 476 477 REQUIRE(managerp != NULL && *managerp == NULL); 478 479 if (timermgr != NULL) { 480 timermgr->refs++; 481 *managerp = (isc_timermgr_t *)timermgr; 482 return (ISC_R_SUCCESS); 483 } 484 485 manager = malloc(sizeof(*manager)); 486 if (manager == NULL) 487 return (ISC_R_NOMEMORY); 488 489 manager->common.impmagic = TIMER_MANAGER_MAGIC; 490 manager->common.magic = ISCAPI_TIMERMGR_MAGIC; 491 manager->done = ISC_FALSE; 492 INIT_LIST(manager->timers); 493 manager->nscheduled = 0; 494 timespecclear(&manager->due); 495 manager->heap = NULL; 496 result = isc_heap_create(sooner, set_index, 0, &manager->heap); 497 if (result != ISC_R_SUCCESS) { 498 INSIST(result == ISC_R_NOMEMORY); 499 free(manager); 500 return (ISC_R_NOMEMORY); 501 } 502 manager->refs = 1; 503 timermgr = manager; 504 505 *managerp = (isc_timermgr_t *)manager; 506 507 return (ISC_R_SUCCESS); 508 } 509 510 void 511 isc__timermgr_destroy(isc_timermgr_t **managerp) { 512 isc__timermgr_t *manager; 513 514 /* 515 * Destroy a timer manager. 516 */ 517 518 REQUIRE(managerp != NULL); 519 manager = (isc__timermgr_t *)*managerp; 520 REQUIRE(VALID_MANAGER(manager)); 521 522 manager->refs--; 523 if (manager->refs > 0) { 524 *managerp = NULL; 525 return; 526 } 527 timermgr = NULL; 528 529 isc__timermgr_dispatch((isc_timermgr_t *)manager); 530 531 REQUIRE(EMPTY(manager->timers)); 532 manager->done = ISC_TRUE; 533 534 /* 535 * Clean up. 536 */ 537 isc_heap_destroy(&manager->heap); 538 manager->common.impmagic = 0; 539 manager->common.magic = 0; 540 free(manager); 541 542 *managerp = NULL; 543 544 timermgr = NULL; 545 } 546 547 isc_result_t 548 isc__timermgr_nextevent(isc_timermgr_t *manager0, struct timespec *when) { 549 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 550 551 if (manager == NULL) 552 manager = timermgr; 553 if (manager == NULL || manager->nscheduled == 0) 554 return (ISC_R_NOTFOUND); 555 *when = manager->due; 556 return (ISC_R_SUCCESS); 557 } 558 559 void 560 isc__timermgr_dispatch(isc_timermgr_t *manager0) { 561 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 562 struct timespec now; 563 564 if (manager == NULL) 565 manager = timermgr; 566 if (manager == NULL) 567 return; 568 clock_gettime(CLOCK_REALTIME, &now); 569 dispatch(manager, &now); 570 } 571 572 isc_result_t 573 isc_timermgr_create(isc_timermgr_t **managerp) { 574 return (isc__timermgr_create(managerp)); 575 } 576 577 void 578 isc_timermgr_destroy(isc_timermgr_t **managerp) { 579 REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp)); 580 581 isc__timermgr_destroy(managerp); 582 583 ENSURE(*managerp == NULL); 584 } 585 586 isc_result_t 587 isc_timer_create(isc_timermgr_t *manager, const struct timespec *interval, 588 isc_task_t *task, isc_taskaction_t action, void *arg, 589 isc_timer_t **timerp) 590 { 591 REQUIRE(ISCAPI_TIMERMGR_VALID(manager)); 592 593 return (isc__timer_create(manager, interval, 594 task, action, arg, timerp)); 595 } 596 597 void 598 isc_timer_detach(isc_timer_t **timerp) { 599 REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp)); 600 601 isc__timer_detach(timerp); 602 603 ENSURE(*timerp == NULL); 604 } 605 606 isc_result_t 607 isc_timer_reset(isc_timer_t *timer, const struct timespec *interval, 608 isc_boolean_t purge) 609 { 610 REQUIRE(ISCAPI_TIMER_VALID(timer)); 611 612 return (isc__timer_reset(timer, interval, purge)); 613 } 614 615 void 616 isc_timer_touch(isc_timer_t *timer) { 617 REQUIRE(ISCAPI_TIMER_VALID(timer)); 618 619 isc__timer_touch(timer); 620 } 621