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