diff options
Diffstat (limited to 'src/backend/utils/misc/timeout.c')
-rw-r--r-- | src/backend/utils/misc/timeout.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c index 3c3e41eca91..aae947e601b 100644 --- a/src/backend/utils/misc/timeout.c +++ b/src/backend/utils/misc/timeout.c @@ -16,6 +16,7 @@ #include <sys/time.h> +#include "miscadmin.h" #include "storage/proc.h" #include "utils/timeout.h" #include "utils/timestamp.h" @@ -260,6 +261,23 @@ handle_sig_alarm(SIGNAL_ARGS) int save_errno = errno; /* + * We may be executing while ImmediateInterruptOK is true (e.g., when + * mainline is waiting for a lock). If SIGINT or similar arrives while + * this code is running, we'd lose control and perhaps leave our data + * structures in an inconsistent state. Hold off interrupts to prevent + * that. + * + * Note: it's possible for a SIGINT to interrupt handle_sig_alarm even + * before we reach HOLD_INTERRUPTS(); the net effect would be as if the + * SIGALRM event had been silently lost. Therefore error recovery must + * include some action that will allow any lost interrupt to be + * rescheduled. Disabling some or all timeouts is sufficient, or if + * that's not appropriate, reschedule_timeouts() can be called. Also, the + * signal blocking hazard described below applies here too. + */ + HOLD_INTERRUPTS(); + + /* * SIGALRM is always cause for waking anything waiting on the process * latch. Cope with MyProc not being there, as the startup process also * uses this signal handler. @@ -311,6 +329,20 @@ handle_sig_alarm(SIGNAL_ARGS) } } + /* + * Re-allow query cancel, and then service any cancel request that arrived + * meanwhile (this might in particular include a cancel request fired by + * one of the timeout handlers). + * + * Note: a longjmp from here is safe so far as our own data structures are + * concerned; but on platforms that block a signal before calling the + * handler and then un-block it on return, longjmping out of the signal + * handler leaves SIGALRM still blocked. Error cleanup is responsible for + * unblocking any blocked signals. + */ + RESUME_INTERRUPTS(); + CHECK_FOR_INTERRUPTS(); + errno = save_errno; } @@ -388,6 +420,30 @@ RegisterTimeout(TimeoutId id, timeout_handler_proc handler) } /* + * Reschedule any pending SIGALRM interrupt. + * + * This can be used during error recovery in case query cancel resulted in loss + * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it + * could do anything). But note it's not necessary if any of the public + * enable_ or disable_timeout functions are called in the same area, since + * those all do schedule_alarm() internally if needed. + */ +void +reschedule_timeouts(void) +{ + /* For flexibility, allow this to be called before we're initialized. */ + if (!all_timeouts_initialized) + return; + + /* Disable timeout interrupts for safety. */ + disable_alarm(); + + /* Reschedule the interrupt, if any timeouts remain active. */ + if (num_active_timeouts > 0) + schedule_alarm(GetCurrentTimestamp()); +} + +/* * Enable the specified timeout to fire after the specified delay. * * Delay is given in milliseconds. |