1 /*------------------------------------------------------------------------- 2 * 3 * timeout.c 4 * Routines to multiplex SIGALRM interrupts for multiple timeout reasons. 5 * 6 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group 7 * Portions Copyright (c) 1994, Regents of the University of California 8 * 9 * 10 * IDENTIFICATION 11 * src/backend/utils/misc/timeout.c 12 * 13 *------------------------------------------------------------------------- 14 */ 15 #include "postgres.h" 16 17 #include <sys/time.h> 18 19 #include "miscadmin.h" 20 #include "storage/proc.h" 21 #include "utils/timeout.h" 22 #include "utils/timestamp.h" 23 24 25 /* Data about any one timeout reason */ 26 typedef struct timeout_params 27 { 28 TimeoutId index; /* identifier of timeout reason */ 29 30 /* volatile because it may be changed from the signal handler */ 31 volatile bool indicator; /* true if timeout has occurred */ 32 33 /* callback function for timeout, or NULL if timeout not registered */ 34 timeout_handler_proc timeout_handler; 35 36 TimestampTz start_time; /* time that timeout was last activated */ 37 TimestampTz fin_time; /* time it is, or was last, due to fire */ 38 } timeout_params; 39 40 /* 41 * List of possible timeout reasons in the order of enum TimeoutId. 42 */ 43 static timeout_params all_timeouts[MAX_TIMEOUTS]; 44 static bool all_timeouts_initialized = false; 45 46 /* 47 * List of active timeouts ordered by their fin_time and priority. 48 * This list is subject to change by the interrupt handler, so it's volatile. 49 */ 50 static volatile int num_active_timeouts = 0; 51 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS]; 52 53 /* 54 * Flag controlling whether the signal handler is allowed to do anything. 55 * We leave this "false" when we're not expecting interrupts, just in case. 56 * 57 * Note that we don't bother to reset any pending timer interrupt when we 58 * disable the signal handler; it's not really worth the cycles to do so, 59 * since the probability of the interrupt actually occurring while we have 60 * it disabled is low. See comments in schedule_alarm() about that. 61 */ 62 static volatile sig_atomic_t alarm_enabled = false; 63 64 #define disable_alarm() (alarm_enabled = false) 65 #define enable_alarm() (alarm_enabled = true) 66 67 68 /***************************************************************************** 69 * Internal helper functions 70 * 71 * For all of these, it is caller's responsibility to protect them from 72 * interruption by the signal handler. Generally, call disable_alarm() 73 * first to prevent interruption, then update state, and last call 74 * schedule_alarm(), which will re-enable the signal handler if needed. 75 *****************************************************************************/ 76 77 /* 78 * Find the index of a given timeout reason in the active array. 79 * If it's not there, return -1. 80 */ 81 static int 82 find_active_timeout(TimeoutId id) 83 { 84 int i; 85 86 for (i = 0; i < num_active_timeouts; i++) 87 { 88 if (active_timeouts[i]->index == id) 89 return i; 90 } 91 92 return -1; 93 } 94 95 /* 96 * Insert specified timeout reason into the list of active timeouts 97 * at the given index. 98 */ 99 static void 100 insert_timeout(TimeoutId id, int index) 101 { 102 int i; 103 104 if (index < 0 || index > num_active_timeouts) 105 elog(FATAL, "timeout index %d out of range 0..%d", index, 106 num_active_timeouts); 107 108 for (i = num_active_timeouts - 1; i >= index; i--) 109 active_timeouts[i + 1] = active_timeouts[i]; 110 111 active_timeouts[index] = &all_timeouts[id]; 112 113 num_active_timeouts++; 114 } 115 116 /* 117 * Remove the index'th element from the timeout list. 118 */ 119 static void 120 remove_timeout_index(int index) 121 { 122 int i; 123 124 if (index < 0 || index >= num_active_timeouts) 125 elog(FATAL, "timeout index %d out of range 0..%d", index, 126 num_active_timeouts - 1); 127 128 for (i = index + 1; i < num_active_timeouts; i++) 129 active_timeouts[i - 1] = active_timeouts[i]; 130 131 num_active_timeouts--; 132 } 133 134 /* 135 * Enable the specified timeout reason 136 */ 137 static void 138 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time) 139 { 140 int i; 141 142 /* Assert request is sane */ 143 Assert(all_timeouts_initialized); 144 Assert(all_timeouts[id].timeout_handler != NULL); 145 146 /* 147 * If this timeout was already active, momentarily disable it. We 148 * interpret the call as a directive to reschedule the timeout. 149 */ 150 i = find_active_timeout(id); 151 if (i >= 0) 152 remove_timeout_index(i); 153 154 /* 155 * Find out the index where to insert the new timeout. We sort by 156 * fin_time, and for equal fin_time by priority. 157 */ 158 for (i = 0; i < num_active_timeouts; i++) 159 { 160 timeout_params *old_timeout = active_timeouts[i]; 161 162 if (fin_time < old_timeout->fin_time) 163 break; 164 if (fin_time == old_timeout->fin_time && id < old_timeout->index) 165 break; 166 } 167 168 /* 169 * Mark the timeout active, and insert it into the active list. 170 */ 171 all_timeouts[id].indicator = false; 172 all_timeouts[id].start_time = now; 173 all_timeouts[id].fin_time = fin_time; 174 175 insert_timeout(id, i); 176 } 177 178 /* 179 * Schedule alarm for the next active timeout, if any 180 * 181 * We assume the caller has obtained the current time, or a close-enough 182 * approximation. 183 */ 184 static void 185 schedule_alarm(TimestampTz now) 186 { 187 if (num_active_timeouts > 0) 188 { 189 struct itimerval timeval; 190 long secs; 191 int usecs; 192 193 MemSet(&timeval, 0, sizeof(struct itimerval)); 194 195 /* Get the time remaining till the nearest pending timeout */ 196 TimestampDifference(now, active_timeouts[0]->fin_time, 197 &secs, &usecs); 198 199 /* 200 * It's possible that the difference is less than a microsecond; 201 * ensure we don't cancel, rather than set, the interrupt. 202 */ 203 if (secs == 0 && usecs == 0) 204 usecs = 1; 205 206 timeval.it_value.tv_sec = secs; 207 timeval.it_value.tv_usec = usecs; 208 209 /* 210 * We must enable the signal handler before calling setitimer(); if we 211 * did it in the other order, we'd have a race condition wherein the 212 * interrupt could occur before we can set alarm_enabled, so that the 213 * signal handler would fail to do anything. 214 * 215 * Because we didn't bother to reset the timer in disable_alarm(), 216 * it's possible that a previously-set interrupt will fire between 217 * enable_alarm() and setitimer(). This is safe, however. There are 218 * two possible outcomes: 219 * 220 * 1. The signal handler finds nothing to do (because the nearest 221 * timeout event is still in the future). It will re-set the timer 222 * and return. Then we'll overwrite the timer value with a new one. 223 * This will mean that the timer fires a little later than we 224 * intended, but only by the amount of time it takes for the signal 225 * handler to do nothing useful, which shouldn't be much. 226 * 227 * 2. The signal handler executes and removes one or more timeout 228 * events. When it returns, either the queue is now empty or the 229 * frontmost event is later than the one we looked at above. So we'll 230 * overwrite the timer value with one that is too soon (plus or minus 231 * the signal handler's execution time), causing a useless interrupt 232 * to occur. But the handler will then re-set the timer and 233 * everything will still work as expected. 234 * 235 * Since these cases are of very low probability (the window here 236 * being quite narrow), it's not worth adding cycles to the mainline 237 * code to prevent occasional wasted interrupts. 238 */ 239 enable_alarm(); 240 241 /* Set the alarm timer */ 242 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0) 243 elog(FATAL, "could not enable SIGALRM timer: %m"); 244 } 245 } 246 247 248 /***************************************************************************** 249 * Signal handler 250 *****************************************************************************/ 251 252 /* 253 * Signal handler for SIGALRM 254 * 255 * Process any active timeout reasons and then reschedule the interrupt 256 * as needed. 257 */ 258 static void 259 handle_sig_alarm(SIGNAL_ARGS) 260 { 261 int save_errno = errno; 262 263 /* 264 * Bump the holdoff counter, to make sure nothing we call will process 265 * interrupts directly. No timeout handler should do that, but these 266 * failures are hard to debug, so better be sure. 267 */ 268 HOLD_INTERRUPTS(); 269 270 /* 271 * SIGALRM is always cause for waking anything waiting on the process 272 * latch. 273 */ 274 SetLatch(MyLatch); 275 276 /* 277 * Fire any pending timeouts, but only if we're enabled to do so. 278 */ 279 if (alarm_enabled) 280 { 281 /* 282 * Disable alarms, just in case this platform allows signal handlers 283 * to interrupt themselves. schedule_alarm() will re-enable if 284 * appropriate. 285 */ 286 disable_alarm(); 287 288 if (num_active_timeouts > 0) 289 { 290 TimestampTz now = GetCurrentTimestamp(); 291 292 /* While the first pending timeout has been reached ... */ 293 while (num_active_timeouts > 0 && 294 now >= active_timeouts[0]->fin_time) 295 { 296 timeout_params *this_timeout = active_timeouts[0]; 297 298 /* Remove it from the active list */ 299 remove_timeout_index(0); 300 301 /* Mark it as fired */ 302 this_timeout->indicator = true; 303 304 /* And call its handler function */ 305 this_timeout->timeout_handler(); 306 307 /* 308 * The handler might not take negligible time (CheckDeadLock 309 * for instance isn't too cheap), so let's update our idea of 310 * "now" after each one. 311 */ 312 now = GetCurrentTimestamp(); 313 } 314 315 /* Done firing timeouts, so reschedule next interrupt if any */ 316 schedule_alarm(now); 317 } 318 } 319 320 RESUME_INTERRUPTS(); 321 322 errno = save_errno; 323 } 324 325 326 /***************************************************************************** 327 * Public API 328 *****************************************************************************/ 329 330 /* 331 * Initialize timeout module. 332 * 333 * This must be called in every process that wants to use timeouts. 334 * 335 * If the process was forked from another one that was also using this 336 * module, be sure to call this before re-enabling signals; else handlers 337 * meant to run in the parent process might get invoked in this one. 338 */ 339 void 340 InitializeTimeouts(void) 341 { 342 int i; 343 344 /* Initialize, or re-initialize, all local state */ 345 disable_alarm(); 346 347 num_active_timeouts = 0; 348 349 for (i = 0; i < MAX_TIMEOUTS; i++) 350 { 351 all_timeouts[i].index = i; 352 all_timeouts[i].indicator = false; 353 all_timeouts[i].timeout_handler = NULL; 354 all_timeouts[i].start_time = 0; 355 all_timeouts[i].fin_time = 0; 356 } 357 358 all_timeouts_initialized = true; 359 360 /* Now establish the signal handler */ 361 pqsignal(SIGALRM, handle_sig_alarm); 362 } 363 364 /* 365 * Register a timeout reason 366 * 367 * For predefined timeouts, this just registers the callback function. 368 * 369 * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and 370 * return a timeout ID. 371 */ 372 TimeoutId 373 RegisterTimeout(TimeoutId id, timeout_handler_proc handler) 374 { 375 Assert(all_timeouts_initialized); 376 377 /* There's no need to disable the signal handler here. */ 378 379 if (id >= USER_TIMEOUT) 380 { 381 /* Allocate a user-defined timeout reason */ 382 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++) 383 if (all_timeouts[id].timeout_handler == NULL) 384 break; 385 if (id >= MAX_TIMEOUTS) 386 ereport(FATAL, 387 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), 388 errmsg("cannot add more timeout reasons"))); 389 } 390 391 Assert(all_timeouts[id].timeout_handler == NULL); 392 393 all_timeouts[id].timeout_handler = handler; 394 395 return id; 396 } 397 398 /* 399 * Reschedule any pending SIGALRM interrupt. 400 * 401 * This can be used during error recovery in case query cancel resulted in loss 402 * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it 403 * could do anything). But note it's not necessary if any of the public 404 * enable_ or disable_timeout functions are called in the same area, since 405 * those all do schedule_alarm() internally if needed. 406 */ 407 void 408 reschedule_timeouts(void) 409 { 410 /* For flexibility, allow this to be called before we're initialized. */ 411 if (!all_timeouts_initialized) 412 return; 413 414 /* Disable timeout interrupts for safety. */ 415 disable_alarm(); 416 417 /* Reschedule the interrupt, if any timeouts remain active. */ 418 if (num_active_timeouts > 0) 419 schedule_alarm(GetCurrentTimestamp()); 420 } 421 422 /* 423 * Enable the specified timeout to fire after the specified delay. 424 * 425 * Delay is given in milliseconds. 426 */ 427 void 428 enable_timeout_after(TimeoutId id, int delay_ms) 429 { 430 TimestampTz now; 431 TimestampTz fin_time; 432 433 /* Disable timeout interrupts for safety. */ 434 disable_alarm(); 435 436 /* Queue the timeout at the appropriate time. */ 437 now = GetCurrentTimestamp(); 438 fin_time = TimestampTzPlusMilliseconds(now, delay_ms); 439 enable_timeout(id, now, fin_time); 440 441 /* Set the timer interrupt. */ 442 schedule_alarm(now); 443 } 444 445 /* 446 * Enable the specified timeout to fire at the specified time. 447 * 448 * This is provided to support cases where there's a reason to calculate 449 * the timeout by reference to some point other than "now". If there isn't, 450 * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice. 451 */ 452 void 453 enable_timeout_at(TimeoutId id, TimestampTz fin_time) 454 { 455 TimestampTz now; 456 457 /* Disable timeout interrupts for safety. */ 458 disable_alarm(); 459 460 /* Queue the timeout at the appropriate time. */ 461 now = GetCurrentTimestamp(); 462 enable_timeout(id, now, fin_time); 463 464 /* Set the timer interrupt. */ 465 schedule_alarm(now); 466 } 467 468 /* 469 * Enable multiple timeouts at once. 470 * 471 * This works like calling enable_timeout_after() and/or enable_timeout_at() 472 * multiple times. Use this to reduce the number of GetCurrentTimestamp() 473 * and setitimer() calls needed to establish multiple timeouts. 474 */ 475 void 476 enable_timeouts(const EnableTimeoutParams *timeouts, int count) 477 { 478 TimestampTz now; 479 int i; 480 481 /* Disable timeout interrupts for safety. */ 482 disable_alarm(); 483 484 /* Queue the timeout(s) at the appropriate times. */ 485 now = GetCurrentTimestamp(); 486 487 for (i = 0; i < count; i++) 488 { 489 TimeoutId id = timeouts[i].id; 490 TimestampTz fin_time; 491 492 switch (timeouts[i].type) 493 { 494 case TMPARAM_AFTER: 495 fin_time = TimestampTzPlusMilliseconds(now, 496 timeouts[i].delay_ms); 497 enable_timeout(id, now, fin_time); 498 break; 499 500 case TMPARAM_AT: 501 enable_timeout(id, now, timeouts[i].fin_time); 502 break; 503 504 default: 505 elog(ERROR, "unrecognized timeout type %d", 506 (int) timeouts[i].type); 507 break; 508 } 509 } 510 511 /* Set the timer interrupt. */ 512 schedule_alarm(now); 513 } 514 515 /* 516 * Cancel the specified timeout. 517 * 518 * The timeout's I've-been-fired indicator is reset, 519 * unless keep_indicator is true. 520 * 521 * When a timeout is canceled, any other active timeout remains in force. 522 * It's not an error to disable a timeout that is not enabled. 523 */ 524 void 525 disable_timeout(TimeoutId id, bool keep_indicator) 526 { 527 int i; 528 529 /* Assert request is sane */ 530 Assert(all_timeouts_initialized); 531 Assert(all_timeouts[id].timeout_handler != NULL); 532 533 /* Disable timeout interrupts for safety. */ 534 disable_alarm(); 535 536 /* Find the timeout and remove it from the active list. */ 537 i = find_active_timeout(id); 538 if (i >= 0) 539 remove_timeout_index(i); 540 541 /* Mark it inactive, whether it was active or not. */ 542 if (!keep_indicator) 543 all_timeouts[id].indicator = false; 544 545 /* Reschedule the interrupt, if any timeouts remain active. */ 546 if (num_active_timeouts > 0) 547 schedule_alarm(GetCurrentTimestamp()); 548 } 549 550 /* 551 * Cancel multiple timeouts at once. 552 * 553 * The timeouts' I've-been-fired indicators are reset, 554 * unless timeouts[i].keep_indicator is true. 555 * 556 * This works like calling disable_timeout() multiple times. 557 * Use this to reduce the number of GetCurrentTimestamp() 558 * and setitimer() calls needed to cancel multiple timeouts. 559 */ 560 void 561 disable_timeouts(const DisableTimeoutParams *timeouts, int count) 562 { 563 int i; 564 565 Assert(all_timeouts_initialized); 566 567 /* Disable timeout interrupts for safety. */ 568 disable_alarm(); 569 570 /* Cancel the timeout(s). */ 571 for (i = 0; i < count; i++) 572 { 573 TimeoutId id = timeouts[i].id; 574 int idx; 575 576 Assert(all_timeouts[id].timeout_handler != NULL); 577 578 idx = find_active_timeout(id); 579 if (idx >= 0) 580 remove_timeout_index(idx); 581 582 if (!timeouts[i].keep_indicator) 583 all_timeouts[id].indicator = false; 584 } 585 586 /* Reschedule the interrupt, if any timeouts remain active. */ 587 if (num_active_timeouts > 0) 588 schedule_alarm(GetCurrentTimestamp()); 589 } 590 591 /* 592 * Disable SIGALRM and remove all timeouts from the active list, 593 * and optionally reset their timeout indicators. 594 */ 595 void 596 disable_all_timeouts(bool keep_indicators) 597 { 598 disable_alarm(); 599 600 /* 601 * Only bother to reset the timer if we think it's active. We could just 602 * let the interrupt happen anyway, but it's probably a bit cheaper to do 603 * setitimer() than to let the useless interrupt happen. 604 */ 605 if (num_active_timeouts > 0) 606 { 607 struct itimerval timeval; 608 609 MemSet(&timeval, 0, sizeof(struct itimerval)); 610 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0) 611 elog(FATAL, "could not disable SIGALRM timer: %m"); 612 } 613 614 num_active_timeouts = 0; 615 616 if (!keep_indicators) 617 { 618 int i; 619 620 for (i = 0; i < MAX_TIMEOUTS; i++) 621 all_timeouts[i].indicator = false; 622 } 623 } 624 625 /* 626 * Return the timeout's I've-been-fired indicator 627 * 628 * If reset_indicator is true, reset the indicator when returning true. 629 * To avoid missing timeouts due to race conditions, we are careful not to 630 * reset the indicator when returning false. 631 */ 632 bool 633 get_timeout_indicator(TimeoutId id, bool reset_indicator) 634 { 635 if (all_timeouts[id].indicator) 636 { 637 if (reset_indicator) 638 all_timeouts[id].indicator = false; 639 return true; 640 } 641 return false; 642 } 643 644 /* 645 * Return the time when the timeout was most recently activated 646 * 647 * Note: will return 0 if timeout has never been activated in this process. 648 * However, we do *not* reset the start_time when a timeout occurs, so as 649 * not to create a race condition if SIGALRM fires just as some code is 650 * about to fetch the value. 651 */ 652 TimestampTz 653 get_timeout_start_time(TimeoutId id) 654 { 655 return all_timeouts[id].start_time; 656 } 657 658 /* 659 * Return the time when the timeout is, or most recently was, due to fire 660 * 661 * Note: will return 0 if timeout has never been activated in this process. 662 * However, we do *not* reset the fin_time when a timeout occurs, so as 663 * not to create a race condition if SIGALRM fires just as some code is 664 * about to fetch the value. 665 */ 666 TimestampTz 667 get_timeout_finish_time(TimeoutId id) 668 { 669 return all_timeouts[id].fin_time; 670 } 671