1 /*-------------------------------------------------------------------------
4 * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/misc/timeout.c
13 *-------------------------------------------------------------------------
19 #include "miscadmin.h"
20 #include "storage/proc.h"
21 #include "utils/timeout.h"
22 #include "utils/timestamp.h"
25 /* Data about any one timeout reason */
26 typedef struct timeout_params
28 TimeoutId index; /* identifier of timeout reason */
30 /* volatile because these may be changed from the signal handler */
31 volatile bool active; /* true if timeout is in active_timeouts[] */
32 volatile bool indicator; /* true if timeout has occurred */
34 /* callback function for timeout, or NULL if timeout not registered */
35 timeout_handler_proc timeout_handler;
37 TimestampTz start_time; /* time that timeout was last activated */
38 TimestampTz fin_time; /* time it is, or was last, due to fire */
42 * List of possible timeout reasons in the order of enum TimeoutId.
44 static timeout_params all_timeouts[MAX_TIMEOUTS];
45 static bool all_timeouts_initialized = false;
48 * List of active timeouts ordered by their fin_time and priority.
49 * This list is subject to change by the interrupt handler, so it's volatile.
51 static volatile int num_active_timeouts = 0;
52 static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
55 * Flag controlling whether the signal handler is allowed to do anything.
56 * We leave this "false" when we're not expecting interrupts, just in case.
58 * Note that we don't bother to reset any pending timer interrupt when we
59 * disable the signal handler; it's not really worth the cycles to do so,
60 * since the probability of the interrupt actually occurring while we have
61 * it disabled is low. See comments in schedule_alarm() about that.
63 static volatile sig_atomic_t alarm_enabled = false;
65 #define disable_alarm() (alarm_enabled = false)
66 #define enable_alarm() (alarm_enabled = true)
69 /*****************************************************************************
70 * Internal helper functions
72 * For all of these, it is caller's responsibility to protect them from
73 * interruption by the signal handler. Generally, call disable_alarm()
74 * first to prevent interruption, then update state, and last call
75 * schedule_alarm(), which will re-enable the signal handler if needed.
76 *****************************************************************************/
79 * Find the index of a given timeout reason in the active array.
80 * If it's not there, return -1.
83 find_active_timeout(TimeoutId id)
87 for (i = 0; i < num_active_timeouts; i++)
89 if (active_timeouts[i]->index == id)
97 * Insert specified timeout reason into the list of active timeouts
101 insert_timeout(TimeoutId id, int index)
105 if (index < 0 || index > num_active_timeouts)
106 elog(FATAL, "timeout index %d out of range 0..%d", index,
107 num_active_timeouts);
109 Assert(!all_timeouts[id].active);
110 all_timeouts[id].active = true;
112 for (i = num_active_timeouts - 1; i >= index; i--)
113 active_timeouts[i + 1] = active_timeouts[i];
115 active_timeouts[index] = &all_timeouts[id];
117 num_active_timeouts++;
121 * Remove the index'th element from the timeout list.
124 remove_timeout_index(int index)
128 if (index < 0 || index >= num_active_timeouts)
129 elog(FATAL, "timeout index %d out of range 0..%d", index,
130 num_active_timeouts - 1);
132 Assert(active_timeouts[index]->active);
133 active_timeouts[index]->active = false;
135 for (i = index + 1; i < num_active_timeouts; i++)
136 active_timeouts[i - 1] = active_timeouts[i];
138 num_active_timeouts--;
142 * Enable the specified timeout reason
145 enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
149 /* Assert request is sane */
150 Assert(all_timeouts_initialized);
151 Assert(all_timeouts[id].timeout_handler != NULL);
154 * If this timeout was already active, momentarily disable it. We
155 * interpret the call as a directive to reschedule the timeout.
157 if (all_timeouts[id].active)
158 remove_timeout_index(find_active_timeout(id));
161 * Find out the index where to insert the new timeout. We sort by
162 * fin_time, and for equal fin_time by priority.
164 for (i = 0; i < num_active_timeouts; i++)
166 timeout_params *old_timeout = active_timeouts[i];
168 if (fin_time < old_timeout->fin_time)
170 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
175 * Mark the timeout active, and insert it into the active list.
177 all_timeouts[id].indicator = false;
178 all_timeouts[id].start_time = now;
179 all_timeouts[id].fin_time = fin_time;
181 insert_timeout(id, i);
185 * Schedule alarm for the next active timeout, if any
187 * We assume the caller has obtained the current time, or a close-enough
191 schedule_alarm(TimestampTz now)
193 if (num_active_timeouts > 0)
195 struct itimerval timeval;
199 MemSet(&timeval, 0, sizeof(struct itimerval));
201 /* Get the time remaining till the nearest pending timeout */
202 TimestampDifference(now, active_timeouts[0]->fin_time,
206 * It's possible that the difference is less than a microsecond;
207 * ensure we don't cancel, rather than set, the interrupt.
209 if (secs == 0 && usecs == 0)
212 timeval.it_value.tv_sec = secs;
213 timeval.it_value.tv_usec = usecs;
216 * We must enable the signal handler before calling setitimer(); if we
217 * did it in the other order, we'd have a race condition wherein the
218 * interrupt could occur before we can set alarm_enabled, so that the
219 * signal handler would fail to do anything.
221 * Because we didn't bother to reset the timer in disable_alarm(),
222 * it's possible that a previously-set interrupt will fire between
223 * enable_alarm() and setitimer(). This is safe, however. There are
224 * two possible outcomes:
226 * 1. The signal handler finds nothing to do (because the nearest
227 * timeout event is still in the future). It will re-set the timer
228 * and return. Then we'll overwrite the timer value with a new one.
229 * This will mean that the timer fires a little later than we
230 * intended, but only by the amount of time it takes for the signal
231 * handler to do nothing useful, which shouldn't be much.
233 * 2. The signal handler executes and removes one or more timeout
234 * events. When it returns, either the queue is now empty or the
235 * frontmost event is later than the one we looked at above. So we'll
236 * overwrite the timer value with one that is too soon (plus or minus
237 * the signal handler's execution time), causing a useless interrupt
238 * to occur. But the handler will then re-set the timer and
239 * everything will still work as expected.
241 * Since these cases are of very low probability (the window here
242 * being quite narrow), it's not worth adding cycles to the mainline
243 * code to prevent occasional wasted interrupts.
247 /* Set the alarm timer */
248 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
249 elog(FATAL, "could not enable SIGALRM timer: %m");
254 /*****************************************************************************
256 *****************************************************************************/
259 * Signal handler for SIGALRM
261 * Process any active timeout reasons and then reschedule the interrupt
265 handle_sig_alarm(SIGNAL_ARGS)
267 int save_errno = errno;
270 * Bump the holdoff counter, to make sure nothing we call will process
271 * interrupts directly. No timeout handler should do that, but these
272 * failures are hard to debug, so better be sure.
277 * SIGALRM is always cause for waking anything waiting on the process
283 * Fire any pending timeouts, but only if we're enabled to do so.
288 * Disable alarms, just in case this platform allows signal handlers
289 * to interrupt themselves. schedule_alarm() will re-enable if
294 if (num_active_timeouts > 0)
296 TimestampTz now = GetCurrentTimestamp();
298 /* While the first pending timeout has been reached ... */
299 while (num_active_timeouts > 0 &&
300 now >= active_timeouts[0]->fin_time)
302 timeout_params *this_timeout = active_timeouts[0];
304 /* Remove it from the active list */
305 remove_timeout_index(0);
307 /* Mark it as fired */
308 this_timeout->indicator = true;
310 /* And call its handler function */
311 this_timeout->timeout_handler();
314 * The handler might not take negligible time (CheckDeadLock
315 * for instance isn't too cheap), so let's update our idea of
316 * "now" after each one.
318 now = GetCurrentTimestamp();
321 /* Done firing timeouts, so reschedule next interrupt if any */
332 /*****************************************************************************
334 *****************************************************************************/
337 * Initialize timeout module.
339 * This must be called in every process that wants to use timeouts.
341 * If the process was forked from another one that was also using this
342 * module, be sure to call this before re-enabling signals; else handlers
343 * meant to run in the parent process might get invoked in this one.
346 InitializeTimeouts(void)
350 /* Initialize, or re-initialize, all local state */
353 num_active_timeouts = 0;
355 for (i = 0; i < MAX_TIMEOUTS; i++)
357 all_timeouts[i].index = i;
358 all_timeouts[i].active = false;
359 all_timeouts[i].indicator = false;
360 all_timeouts[i].timeout_handler = NULL;
361 all_timeouts[i].start_time = 0;
362 all_timeouts[i].fin_time = 0;
365 all_timeouts_initialized = true;
367 /* Now establish the signal handler */
368 pqsignal(SIGALRM, handle_sig_alarm);
372 * Register a timeout reason
374 * For predefined timeouts, this just registers the callback function.
376 * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
377 * return a timeout ID.
380 RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
382 Assert(all_timeouts_initialized);
384 /* There's no need to disable the signal handler here. */
386 if (id >= USER_TIMEOUT)
388 /* Allocate a user-defined timeout reason */
389 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
390 if (all_timeouts[id].timeout_handler == NULL)
392 if (id >= MAX_TIMEOUTS)
394 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
395 errmsg("cannot add more timeout reasons")));
398 Assert(all_timeouts[id].timeout_handler == NULL);
400 all_timeouts[id].timeout_handler = handler;
406 * Reschedule any pending SIGALRM interrupt.
408 * This can be used during error recovery in case query cancel resulted in loss
409 * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it
410 * could do anything). But note it's not necessary if any of the public
411 * enable_ or disable_timeout functions are called in the same area, since
412 * those all do schedule_alarm() internally if needed.
415 reschedule_timeouts(void)
417 /* For flexibility, allow this to be called before we're initialized. */
418 if (!all_timeouts_initialized)
421 /* Disable timeout interrupts for safety. */
424 /* Reschedule the interrupt, if any timeouts remain active. */
425 if (num_active_timeouts > 0)
426 schedule_alarm(GetCurrentTimestamp());
430 * Enable the specified timeout to fire after the specified delay.
432 * Delay is given in milliseconds.
435 enable_timeout_after(TimeoutId id, int delay_ms)
438 TimestampTz fin_time;
440 /* Disable timeout interrupts for safety. */
443 /* Queue the timeout at the appropriate time. */
444 now = GetCurrentTimestamp();
445 fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
446 enable_timeout(id, now, fin_time);
448 /* Set the timer interrupt. */
453 * Enable the specified timeout to fire at the specified time.
455 * This is provided to support cases where there's a reason to calculate
456 * the timeout by reference to some point other than "now". If there isn't,
457 * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
460 enable_timeout_at(TimeoutId id, TimestampTz fin_time)
464 /* Disable timeout interrupts for safety. */
467 /* Queue the timeout at the appropriate time. */
468 now = GetCurrentTimestamp();
469 enable_timeout(id, now, fin_time);
471 /* Set the timer interrupt. */
476 * Enable multiple timeouts at once.
478 * This works like calling enable_timeout_after() and/or enable_timeout_at()
479 * multiple times. Use this to reduce the number of GetCurrentTimestamp()
480 * and setitimer() calls needed to establish multiple timeouts.
483 enable_timeouts(const EnableTimeoutParams *timeouts, int count)
488 /* Disable timeout interrupts for safety. */
491 /* Queue the timeout(s) at the appropriate times. */
492 now = GetCurrentTimestamp();
494 for (i = 0; i < count; i++)
496 TimeoutId id = timeouts[i].id;
497 TimestampTz fin_time;
499 switch (timeouts[i].type)
502 fin_time = TimestampTzPlusMilliseconds(now,
503 timeouts[i].delay_ms);
504 enable_timeout(id, now, fin_time);
508 enable_timeout(id, now, timeouts[i].fin_time);
512 elog(ERROR, "unrecognized timeout type %d",
513 (int) timeouts[i].type);
518 /* Set the timer interrupt. */
523 * Cancel the specified timeout.
525 * The timeout's I've-been-fired indicator is reset,
526 * unless keep_indicator is true.
528 * When a timeout is canceled, any other active timeout remains in force.
529 * It's not an error to disable a timeout that is not enabled.
532 disable_timeout(TimeoutId id, bool keep_indicator)
534 /* Assert request is sane */
535 Assert(all_timeouts_initialized);
536 Assert(all_timeouts[id].timeout_handler != NULL);
538 /* Disable timeout interrupts for safety. */
541 /* Find the timeout and remove it from the active list. */
542 if (all_timeouts[id].active)
543 remove_timeout_index(find_active_timeout(id));
545 /* Mark it inactive, whether it was active or not. */
547 all_timeouts[id].indicator = false;
549 /* Reschedule the interrupt, if any timeouts remain active. */
550 if (num_active_timeouts > 0)
551 schedule_alarm(GetCurrentTimestamp());
555 * Cancel multiple timeouts at once.
557 * The timeouts' I've-been-fired indicators are reset,
558 * unless timeouts[i].keep_indicator is true.
560 * This works like calling disable_timeout() multiple times.
561 * Use this to reduce the number of GetCurrentTimestamp()
562 * and setitimer() calls needed to cancel multiple timeouts.
565 disable_timeouts(const DisableTimeoutParams *timeouts, int count)
569 Assert(all_timeouts_initialized);
571 /* Disable timeout interrupts for safety. */
574 /* Cancel the timeout(s). */
575 for (i = 0; i < count; i++)
577 TimeoutId id = timeouts[i].id;
579 Assert(all_timeouts[id].timeout_handler != NULL);
581 if (all_timeouts[id].active)
582 remove_timeout_index(find_active_timeout(id));
584 if (!timeouts[i].keep_indicator)
585 all_timeouts[id].indicator = false;
588 /* Reschedule the interrupt, if any timeouts remain active. */
589 if (num_active_timeouts > 0)
590 schedule_alarm(GetCurrentTimestamp());
594 * Disable SIGALRM and remove all timeouts from the active list,
595 * and optionally reset their timeout indicators.
598 disable_all_timeouts(bool keep_indicators)
605 * Only bother to reset the timer if we think it's active. We could just
606 * let the interrupt happen anyway, but it's probably a bit cheaper to do
607 * setitimer() than to let the useless interrupt happen.
609 if (num_active_timeouts > 0)
611 struct itimerval timeval;
613 MemSet(&timeval, 0, sizeof(struct itimerval));
614 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
615 elog(FATAL, "could not disable SIGALRM timer: %m");
618 num_active_timeouts = 0;
620 for (i = 0; i < MAX_TIMEOUTS; i++)
622 all_timeouts[i].active = false;
623 if (!keep_indicators)
624 all_timeouts[i].indicator = false;
629 * Return true if the timeout is active (enabled and not yet fired)
631 * This is, of course, subject to race conditions, as the timeout could fire
632 * immediately after we look.
635 get_timeout_active(TimeoutId id)
637 return all_timeouts[id].active;
641 * Return the timeout's I've-been-fired indicator
643 * If reset_indicator is true, reset the indicator when returning true.
644 * To avoid missing timeouts due to race conditions, we are careful not to
645 * reset the indicator when returning false.
648 get_timeout_indicator(TimeoutId id, bool reset_indicator)
650 if (all_timeouts[id].indicator)
653 all_timeouts[id].indicator = false;
660 * Return the time when the timeout was most recently activated
662 * Note: will return 0 if timeout has never been activated in this process.
663 * However, we do *not* reset the start_time when a timeout occurs, so as
664 * not to create a race condition if SIGALRM fires just as some code is
665 * about to fetch the value.
668 get_timeout_start_time(TimeoutId id)
670 return all_timeouts[id].start_time;
674 * Return the time when the timeout is, or most recently was, due to fire
676 * Note: will return 0 if timeout has never been activated in this process.
677 * However, we do *not* reset the fin_time when a timeout occurs, so as
678 * not to create a race condition if SIGALRM fires just as some code is
679 * about to fetch the value.
682 get_timeout_finish_time(TimeoutId id)
684 return all_timeouts[id].fin_time;