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.9 2020/02/16 08:05:41 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 isc_time_t idle; 45 /*! Locked by manager lock. */ 46 isc_timertype_t type; 47 isc_time_t expires; 48 interval_t interval; 49 isc_task_t * task; 50 isc_taskaction_t action; 51 void * arg; 52 unsigned int index; 53 isc_time_t due; 54 LINK(isc__timer_t) link; 55 }; 56 57 #define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M') 58 #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC) 59 60 struct isc__timermgr { 61 /* Not locked. */ 62 isc_timermgr_t common; 63 /* Locked by manager lock. */ 64 isc_boolean_t done; 65 LIST(isc__timer_t) timers; 66 unsigned int nscheduled; 67 isc_time_t due; 68 unsigned int refs; 69 isc_heap_t * heap; 70 }; 71 72 /*% 73 * The following are intended for internal use (indicated by "isc__" 74 * prefix) but are not declared as static, allowing direct access from 75 * unit tests etc. 76 */ 77 78 isc_result_t 79 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type, 80 const isc_time_t *expires, const interval_t *interval, 81 isc_task_t *task, isc_taskaction_t action, void *arg, 82 isc_timer_t **timerp); 83 isc_result_t 84 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type, 85 const isc_time_t *expires, const interval_t *interval, 86 isc_boolean_t purge); 87 isc_timertype_t 88 isc_timer_gettype(isc_timer_t *timer); 89 isc_result_t 90 isc__timer_touch(isc_timer_t *timer); 91 void 92 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp); 93 void 94 isc__timer_detach(isc_timer_t **timerp); 95 isc_result_t 96 isc__timermgr_create(isc_timermgr_t **managerp); 97 void 98 isc__timermgr_destroy(isc_timermgr_t **managerp); 99 100 /*! 101 * If the manager is supposed to be shared, there can be only one. 102 */ 103 static isc__timermgr_t *timermgr = NULL; 104 105 static inline isc_result_t 106 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) { 107 isc_result_t result; 108 isc__timermgr_t *manager; 109 isc_time_t due; 110 int cmp; 111 112 /*! 113 * Note: the caller must ensure locking. 114 */ 115 116 UNUSED(signal_ok); 117 118 manager = timer->manager; 119 120 /* 121 * Compute the new due time. 122 */ 123 if (isc_time_isepoch(&timer->idle)) 124 due = timer->expires; 125 else if (isc_time_isepoch(&timer->expires)) 126 due = timer->idle; 127 else if (isc_time_compare(&timer->idle, &timer->expires) < 0) 128 due = timer->idle; 129 else 130 due = timer->expires; 131 132 /* 133 * Schedule the timer. 134 */ 135 136 if (timer->index > 0) { 137 /* 138 * Already scheduled. 139 */ 140 cmp = isc_time_compare(&due, &timer->due); 141 timer->due = due; 142 switch (cmp) { 143 case -1: 144 isc_heap_increased(manager->heap, timer->index); 145 break; 146 case 1: 147 isc_heap_decreased(manager->heap, timer->index); 148 break; 149 case 0: 150 /* Nothing to do. */ 151 break; 152 } 153 } else { 154 timer->due = due; 155 result = isc_heap_insert(manager->heap, timer); 156 if (result != ISC_R_SUCCESS) { 157 INSIST(result == ISC_R_NOMEMORY); 158 return (ISC_R_NOMEMORY); 159 } 160 manager->nscheduled++; 161 } 162 163 /* 164 * If this timer is at the head of the queue, we need to ensure 165 * that we won't miss it if it has a more recent due time than 166 * the current "next" timer. We do this either by waking up the 167 * run thread, or explicitly setting the value in the manager. 168 */ 169 if (timer->index == 1 && 170 isc_time_compare(&timer->due, &manager->due) < 0) 171 manager->due = timer->due; 172 173 return (ISC_R_SUCCESS); 174 } 175 176 static inline void 177 deschedule(isc__timer_t *timer) { 178 isc__timermgr_t *manager; 179 180 /* 181 * The caller must ensure locking. 182 */ 183 184 manager = timer->manager; 185 if (timer->index > 0) { 186 isc_heap_delete(manager->heap, timer->index); 187 timer->index = 0; 188 INSIST(manager->nscheduled > 0); 189 manager->nscheduled--; 190 } 191 } 192 193 static void 194 destroy(isc__timer_t *timer) { 195 isc__timermgr_t *manager = timer->manager; 196 197 /* 198 * The caller must ensure it is safe to destroy the timer. 199 */ 200 201 (void)isc_task_purgerange(timer->task, 202 timer, 203 ISC_TIMEREVENT_FIRSTEVENT, 204 ISC_TIMEREVENT_LASTEVENT, 205 NULL); 206 deschedule(timer); 207 UNLINK(manager->timers, timer, link); 208 209 isc_task_detach(&timer->task); 210 timer->common.impmagic = 0; 211 timer->common.magic = 0; 212 free(timer); 213 } 214 215 isc_result_t 216 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type, 217 const isc_time_t *expires, const interval_t *interval, 218 isc_task_t *task, isc_taskaction_t action, void *arg, 219 isc_timer_t **timerp) 220 { 221 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 222 isc__timer_t *timer; 223 isc_result_t result; 224 isc_time_t now; 225 226 /* 227 * Create a new 'type' timer managed by 'manager'. The timers 228 * parameters are specified by 'expires' and 'interval'. Events 229 * will be posted to 'task' and when dispatched 'action' will be 230 * called with 'arg' as the arg value. The new timer is returned 231 * in 'timerp'. 232 */ 233 234 REQUIRE(VALID_MANAGER(manager)); 235 REQUIRE(task != NULL); 236 REQUIRE(action != NULL); 237 if (expires == NULL) 238 expires = isc_time_epoch; 239 if (interval == NULL) 240 interval = interval_zero; 241 REQUIRE(!(isc_time_isepoch(expires) && interval_iszero(interval))); 242 REQUIRE(timerp != NULL && *timerp == NULL); 243 REQUIRE(!(isc_time_isepoch(expires) || interval_iszero(interval))); 244 245 /* 246 * Get current time. 247 */ 248 TIME_NOW(&now); 249 250 timer = malloc(sizeof(*timer)); 251 if (timer == NULL) 252 return (ISC_R_NOMEMORY); 253 254 timer->manager = manager; 255 timer->references = 1; 256 257 if (type == isc_timertype_once && !interval_iszero(interval)) { 258 result = isc_time_add(&now, interval, &timer->idle); 259 if (result != ISC_R_SUCCESS) { 260 free(timer); 261 return (result); 262 } 263 } 264 265 timer->type = type; 266 timer->expires = *expires; 267 timer->interval = *interval; 268 timer->task = NULL; 269 isc_task_attach(task, &timer->task); 270 timer->action = action; 271 /* 272 * Removing the const attribute from "arg" is the best of two 273 * evils here. If the timer->arg member is made const, then 274 * it affects a great many recipients of the timer event 275 * which did not pass in an "arg" that was truly const. 276 * Changing isc_timer_create() to not have "arg" prototyped as const, 277 * though, can cause compilers warnings for calls that *do* 278 * have a truly const arg. The caller will have to carefully 279 * keep track of whether arg started as a true const. 280 */ 281 DE_CONST(arg, timer->arg); 282 timer->index = 0; 283 ISC_LINK_INIT(timer, link); 284 timer->common.impmagic = TIMER_MAGIC; 285 timer->common.magic = ISCAPI_TIMER_MAGIC; 286 287 result = schedule(timer, &now, ISC_TRUE); 288 if (result == ISC_R_SUCCESS) 289 APPEND(manager->timers, timer, link); 290 291 if (result != ISC_R_SUCCESS) { 292 timer->common.impmagic = 0; 293 timer->common.magic = 0; 294 isc_task_detach(&timer->task); 295 free(timer); 296 return (result); 297 } 298 299 *timerp = (isc_timer_t *)timer; 300 301 return (ISC_R_SUCCESS); 302 } 303 304 isc_result_t 305 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type, 306 const isc_time_t *expires, const interval_t *interval, 307 isc_boolean_t purge) 308 { 309 isc__timer_t *timer = (isc__timer_t *)timer0; 310 isc_time_t now; 311 isc__timermgr_t *manager; 312 isc_result_t result; 313 314 /* 315 * Change the timer's type, expires, and interval values to the given 316 * values. If 'purge' is ISC_TRUE, any pending events from this timer 317 * are purged from its task's event queue. 318 */ 319 320 REQUIRE(VALID_TIMER(timer)); 321 manager = timer->manager; 322 REQUIRE(VALID_MANAGER(manager)); 323 324 if (expires == NULL) 325 expires = isc_time_epoch; 326 if (interval == NULL) 327 interval = interval_zero; 328 REQUIRE(!(isc_time_isepoch(expires) && interval_iszero(interval))); 329 REQUIRE(!(isc_time_isepoch(expires) || interval_iszero(interval))); 330 331 /* 332 * Get current time. 333 */ 334 TIME_NOW(&now); 335 336 if (purge) 337 (void)isc_task_purgerange(timer->task, 338 timer, 339 ISC_TIMEREVENT_FIRSTEVENT, 340 ISC_TIMEREVENT_LASTEVENT, 341 NULL); 342 timer->type = type; 343 timer->expires = *expires; 344 timer->interval = *interval; 345 if (type == isc_timertype_once && !interval_iszero(interval)) { 346 result = isc_time_add(&now, interval, &timer->idle); 347 } else { 348 isc_time_settoepoch(&timer->idle); 349 result = ISC_R_SUCCESS; 350 } 351 352 if (result == ISC_R_SUCCESS) { 353 result = schedule(timer, &now, ISC_TRUE); 354 } 355 356 return (result); 357 } 358 359 isc_timertype_t 360 isc_timer_gettype(isc_timer_t *timer0) { 361 isc__timer_t *timer = (isc__timer_t *)timer0; 362 isc_timertype_t t; 363 364 REQUIRE(VALID_TIMER(timer)); 365 366 t = timer->type; 367 368 return (t); 369 } 370 371 isc_result_t 372 isc__timer_touch(isc_timer_t *timer0) { 373 isc__timer_t *timer = (isc__timer_t *)timer0; 374 isc_result_t result; 375 isc_time_t now; 376 377 /* 378 * Set the last-touched time of 'timer' to the current time. 379 */ 380 381 REQUIRE(VALID_TIMER(timer)); 382 383 TIME_NOW(&now); 384 result = isc_time_add(&now, &timer->interval, &timer->idle); 385 386 return (result); 387 } 388 389 void 390 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) { 391 isc__timer_t *timer = (isc__timer_t *)timer0; 392 393 /* 394 * Attach *timerp to timer. 395 */ 396 397 REQUIRE(VALID_TIMER(timer)); 398 REQUIRE(timerp != NULL && *timerp == NULL); 399 400 timer->references++; 401 402 *timerp = (isc_timer_t *)timer; 403 } 404 405 void 406 isc__timer_detach(isc_timer_t **timerp) { 407 isc__timer_t *timer; 408 isc_boolean_t free_timer = ISC_FALSE; 409 410 /* 411 * Detach *timerp from its timer. 412 */ 413 414 REQUIRE(timerp != NULL); 415 timer = (isc__timer_t *)*timerp; 416 REQUIRE(VALID_TIMER(timer)); 417 418 REQUIRE(timer->references > 0); 419 timer->references--; 420 if (timer->references == 0) 421 free_timer = ISC_TRUE; 422 423 if (free_timer) 424 destroy(timer); 425 426 *timerp = NULL; 427 } 428 429 static void 430 dispatch(isc__timermgr_t *manager, isc_time_t *now) { 431 isc_boolean_t done = ISC_FALSE, post_event, need_schedule; 432 isc_timerevent_t *event; 433 isc_eventtype_t type = 0; 434 isc__timer_t *timer; 435 isc_result_t result; 436 isc_boolean_t idle; 437 438 /*! 439 * The caller must be holding the manager lock. 440 */ 441 442 while (manager->nscheduled > 0 && !done) { 443 timer = isc_heap_element(manager->heap, 1); 444 INSIST(timer != NULL); 445 if (isc_time_compare(now, &timer->due) >= 0) { 446 if (!isc_time_isepoch(&timer->expires) && 447 isc_time_compare(now, 448 &timer->expires) >= 0) { 449 type = ISC_TIMEREVENT_LIFE; 450 post_event = ISC_TRUE; 451 need_schedule = ISC_FALSE; 452 } else { 453 idle = ISC_FALSE; 454 455 if (!isc_time_isepoch(&timer->idle) && 456 isc_time_compare(now, 457 &timer->idle) >= 0) { 458 idle = ISC_TRUE; 459 } 460 if (idle) { 461 type = ISC_TIMEREVENT_IDLE; 462 post_event = ISC_TRUE; 463 need_schedule = ISC_FALSE; 464 } else { 465 /* 466 * Idle timer has been touched; 467 * reschedule. 468 */ 469 post_event = ISC_FALSE; 470 need_schedule = ISC_TRUE; 471 } 472 } 473 474 if (post_event) { 475 /* 476 * XXX We could preallocate this event. 477 */ 478 event = (isc_timerevent_t *)isc_event_allocate( 479 timer, 480 type, 481 timer->action, 482 timer->arg, 483 sizeof(*event)); 484 485 if (event != NULL) { 486 event->due = timer->due; 487 isc_task_send(timer->task, 488 ISC_EVENT_PTR(&event)); 489 } else 490 UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", 491 "couldn't allocate event"); 492 } 493 494 timer->index = 0; 495 isc_heap_delete(manager->heap, 1); 496 manager->nscheduled--; 497 498 if (need_schedule) { 499 result = schedule(timer, now, ISC_FALSE); 500 if (result != ISC_R_SUCCESS) 501 UNEXPECTED_ERROR(__FILE__, __LINE__, 502 "%s: %u", 503 "couldn't schedule timer", 504 result); 505 } 506 } else { 507 manager->due = timer->due; 508 done = ISC_TRUE; 509 } 510 } 511 } 512 513 static isc_boolean_t 514 sooner(void *v1, void *v2) { 515 isc__timer_t *t1, *t2; 516 517 t1 = v1; 518 t2 = v2; 519 REQUIRE(VALID_TIMER(t1)); 520 REQUIRE(VALID_TIMER(t2)); 521 522 if (isc_time_compare(&t1->due, &t2->due) < 0) 523 return (ISC_TRUE); 524 return (ISC_FALSE); 525 } 526 527 static void 528 set_index(void *what, unsigned int index) { 529 isc__timer_t *timer; 530 531 timer = what; 532 REQUIRE(VALID_TIMER(timer)); 533 534 timer->index = index; 535 } 536 537 isc_result_t 538 isc__timermgr_create(isc_timermgr_t **managerp) { 539 isc__timermgr_t *manager; 540 isc_result_t result; 541 542 /* 543 * Create a timer manager. 544 */ 545 546 REQUIRE(managerp != NULL && *managerp == NULL); 547 548 if (timermgr != NULL) { 549 timermgr->refs++; 550 *managerp = (isc_timermgr_t *)timermgr; 551 return (ISC_R_SUCCESS); 552 } 553 554 manager = malloc(sizeof(*manager)); 555 if (manager == NULL) 556 return (ISC_R_NOMEMORY); 557 558 manager->common.impmagic = TIMER_MANAGER_MAGIC; 559 manager->common.magic = ISCAPI_TIMERMGR_MAGIC; 560 manager->done = ISC_FALSE; 561 INIT_LIST(manager->timers); 562 manager->nscheduled = 0; 563 isc_time_settoepoch(&manager->due); 564 manager->heap = NULL; 565 result = isc_heap_create(sooner, set_index, 0, &manager->heap); 566 if (result != ISC_R_SUCCESS) { 567 INSIST(result == ISC_R_NOMEMORY); 568 free(manager); 569 return (ISC_R_NOMEMORY); 570 } 571 manager->refs = 1; 572 timermgr = manager; 573 574 *managerp = (isc_timermgr_t *)manager; 575 576 return (ISC_R_SUCCESS); 577 } 578 579 void 580 isc__timermgr_destroy(isc_timermgr_t **managerp) { 581 isc__timermgr_t *manager; 582 583 /* 584 * Destroy a timer manager. 585 */ 586 587 REQUIRE(managerp != NULL); 588 manager = (isc__timermgr_t *)*managerp; 589 REQUIRE(VALID_MANAGER(manager)); 590 591 manager->refs--; 592 if (manager->refs > 0) { 593 *managerp = NULL; 594 return; 595 } 596 timermgr = NULL; 597 598 isc__timermgr_dispatch((isc_timermgr_t *)manager); 599 600 REQUIRE(EMPTY(manager->timers)); 601 manager->done = ISC_TRUE; 602 603 /* 604 * Clean up. 605 */ 606 isc_heap_destroy(&manager->heap); 607 manager->common.impmagic = 0; 608 manager->common.magic = 0; 609 free(manager); 610 611 *managerp = NULL; 612 613 timermgr = NULL; 614 } 615 616 isc_result_t 617 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) { 618 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 619 620 if (manager == NULL) 621 manager = timermgr; 622 if (manager == NULL || manager->nscheduled == 0) 623 return (ISC_R_NOTFOUND); 624 *when = manager->due; 625 return (ISC_R_SUCCESS); 626 } 627 628 void 629 isc__timermgr_dispatch(isc_timermgr_t *manager0) { 630 isc__timermgr_t *manager = (isc__timermgr_t *)manager0; 631 isc_time_t now; 632 633 if (manager == NULL) 634 manager = timermgr; 635 if (manager == NULL) 636 return; 637 TIME_NOW(&now); 638 dispatch(manager, &now); 639 } 640 641 isc_result_t 642 isc_timermgr_create(isc_timermgr_t **managerp) { 643 return (isc__timermgr_create(managerp)); 644 } 645 646 void 647 isc_timermgr_destroy(isc_timermgr_t **managerp) { 648 REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp)); 649 650 isc__timermgr_destroy(managerp); 651 652 ENSURE(*managerp == NULL); 653 } 654 655 isc_result_t 656 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type, 657 const isc_time_t *expires, const interval_t *interval, 658 isc_task_t *task, isc_taskaction_t action, void *arg, 659 isc_timer_t **timerp) 660 { 661 REQUIRE(ISCAPI_TIMERMGR_VALID(manager)); 662 663 return (isc__timer_create(manager, type, expires, interval, 664 task, action, arg, timerp)); 665 } 666 667 void 668 isc_timer_detach(isc_timer_t **timerp) { 669 REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp)); 670 671 isc__timer_detach(timerp); 672 673 ENSURE(*timerp == NULL); 674 } 675 676 isc_result_t 677 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type, 678 const isc_time_t *expires, const interval_t *interval, 679 isc_boolean_t purge) 680 { 681 REQUIRE(ISCAPI_TIMER_VALID(timer)); 682 683 return (isc__timer_reset(timer, type, expires, 684 interval, purge)); 685 } 686 687 isc_result_t 688 isc_timer_touch(isc_timer_t *timer) { 689 REQUIRE(ISCAPI_TIMER_VALID(timer)); 690 691 return (isc__timer_touch(timer)); 692 } 693