Commit f34c68f0 authored by Alvaro Herrera's avatar Alvaro Herrera

Introduce timeout handling framework

Management of timeouts was getting a little cumbersome; what we
originally had was more than enough back when we were only concerned
about deadlocks and query cancel; however, when we added timeouts for
standby processes, the code got considerably messier.  Since there are
plans to add more complex timeouts, this seems a good time to introduce
a central timeout handling module.

External modules register their timeout handlers during process
initialization, and later enable and disable them as they see fit using
a simple API; timeout.c is in charge of keeping track of which timeouts
are in effect at any time, installing a common SIGALRM signal handler,
and calling setitimer() as appropriate to ensure timely firing of
external handlers.

timeout.c additionally supports pluggable modules to add their own
timeouts, though this capability isn't exercised anywhere yet.

Additionally, as of this commit, walsender processes are aware of
timeouts; we had a preexisting bug there that made those ignore SIGALRM,
thus being subject to unhandled deadlocks, particularly during the
authentication phase.  This has already been fixed in back branches in
commit 0bf8eb2a, which see for more details.

Main author: Zoltán Böszörményi
Some review and cleanup by Álvaro Herrera
Extensive reworking by Tom Lane
parent dd16f948
...@@ -97,6 +97,7 @@ ...@@ -97,6 +97,7 @@
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/timeout.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
#include "utils/tqual.h" #include "utils/tqual.h"
...@@ -432,7 +433,7 @@ AutoVacLauncherMain(int argc, char *argv[]) ...@@ -432,7 +433,7 @@ AutoVacLauncherMain(int argc, char *argv[])
pqsignal(SIGTERM, avl_sigterm_handler); pqsignal(SIGTERM, avl_sigterm_handler);
pqsignal(SIGQUIT, quickdie); pqsignal(SIGQUIT, quickdie);
pqsignal(SIGALRM, handle_sig_alarm); InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, procsignal_sigusr1_handler); pqsignal(SIGUSR1, procsignal_sigusr1_handler);
...@@ -482,9 +483,9 @@ AutoVacLauncherMain(int argc, char *argv[]) ...@@ -482,9 +483,9 @@ AutoVacLauncherMain(int argc, char *argv[])
/* Prevents interrupts while cleaning up */ /* Prevents interrupts while cleaning up */
HOLD_INTERRUPTS(); HOLD_INTERRUPTS();
/* Forget any pending QueryCancel request */ /* Forget any pending QueryCancel or timeout request */
QueryCancelPending = false; QueryCancelPending = false;
disable_sig_alarm(true); disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */ QueryCancelPending = false; /* again in case timeout occurred */
/* Report the error to the server log */ /* Report the error to the server log */
...@@ -1492,7 +1493,7 @@ AutoVacWorkerMain(int argc, char *argv[]) ...@@ -1492,7 +1493,7 @@ AutoVacWorkerMain(int argc, char *argv[])
pqsignal(SIGINT, StatementCancelHandler); pqsignal(SIGINT, StatementCancelHandler);
pqsignal(SIGTERM, die); pqsignal(SIGTERM, die);
pqsignal(SIGQUIT, quickdie); pqsignal(SIGQUIT, quickdie);
pqsignal(SIGALRM, handle_sig_alarm); InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, procsignal_sigusr1_handler); pqsignal(SIGUSR1, procsignal_sigusr1_handler);
......
...@@ -112,12 +112,12 @@ ...@@ -112,12 +112,12 @@
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/timeout.h"
#ifdef EXEC_BACKEND #ifdef EXEC_BACKEND
#include "storage/spin.h" #include "storage/spin.h"
...@@ -337,6 +337,7 @@ static void reaper(SIGNAL_ARGS); ...@@ -337,6 +337,7 @@ static void reaper(SIGNAL_ARGS);
static void sigusr1_handler(SIGNAL_ARGS); static void sigusr1_handler(SIGNAL_ARGS);
static void startup_die(SIGNAL_ARGS); static void startup_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS); static void dummy_handler(SIGNAL_ARGS);
static void StartupPacketTimeoutHandler(void);
static void CleanupBackend(int pid, int exitstatus); static void CleanupBackend(int pid, int exitstatus);
static void HandleChildCrash(int pid, int exitstatus, const char *procname); static void HandleChildCrash(int pid, int exitstatus, const char *procname);
static void LogChildExit(int lev, const char *procname, static void LogChildExit(int lev, const char *procname,
...@@ -3415,7 +3416,7 @@ BackendInitialize(Port *port) ...@@ -3415,7 +3416,7 @@ BackendInitialize(Port *port)
*/ */
pqsignal(SIGTERM, startup_die); pqsignal(SIGTERM, startup_die);
pqsignal(SIGQUIT, startup_die); pqsignal(SIGQUIT, startup_die);
pqsignal(SIGALRM, startup_die); InitializeTimeouts(); /* establishes SIGALRM handler */
PG_SETMASK(&StartupBlockSig); PG_SETMASK(&StartupBlockSig);
/* /*
...@@ -3469,9 +3470,18 @@ BackendInitialize(Port *port) ...@@ -3469,9 +3470,18 @@ BackendInitialize(Port *port)
* time delay, so that a broken client can't hog a connection * time delay, so that a broken client can't hog a connection
* indefinitely. PreAuthDelay and any DNS interactions above don't count * indefinitely. PreAuthDelay and any DNS interactions above don't count
* against the time limit. * against the time limit.
*
* Note: AuthenticationTimeout is applied here while waiting for the
* startup packet, and then again in InitPostgres for the duration of any
* authentication operations. So a hostile client could tie up the
* process for nearly twice AuthenticationTimeout before we kick him off.
*
* Note: because PostgresMain will call InitializeTimeouts again, the
* registration of STARTUP_PACKET_TIMEOUT will be lost. This is okay
* since we never use it again after this function.
*/ */
if (!enable_sig_alarm(AuthenticationTimeout * 1000, false)) RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
elog(FATAL, "could not set timer for startup packet timeout"); enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
/* /*
* Receive the startup packet (which might turn out to be a cancel request * Receive the startup packet (which might turn out to be a cancel request
...@@ -3508,8 +3518,7 @@ BackendInitialize(Port *port) ...@@ -3508,8 +3518,7 @@ BackendInitialize(Port *port)
/* /*
* Disable the timeout, and prevent SIGTERM/SIGQUIT again. * Disable the timeout, and prevent SIGTERM/SIGQUIT again.
*/ */
if (!disable_sig_alarm(false)) disable_timeout(STARTUP_PACKET_TIMEOUT, false);
elog(FATAL, "could not disable timer for startup packet timeout");
PG_SETMASK(&BlockSig); PG_SETMASK(&BlockSig);
} }
...@@ -4311,8 +4320,8 @@ sigusr1_handler(SIGNAL_ARGS) ...@@ -4311,8 +4320,8 @@ sigusr1_handler(SIGNAL_ARGS)
} }
/* /*
* Timeout or shutdown signal from postmaster while processing startup packet. * SIGTERM or SIGQUIT while processing startup packet.
* Cleanup and exit(1). * Clean up and exit(1).
* *
* XXX: possible future improvement: try to send a message indicating * XXX: possible future improvement: try to send a message indicating
* why we are disconnecting. Problem is to be sure we don't block while * why we are disconnecting. Problem is to be sure we don't block while
...@@ -4339,6 +4348,17 @@ dummy_handler(SIGNAL_ARGS) ...@@ -4339,6 +4348,17 @@ dummy_handler(SIGNAL_ARGS)
{ {
} }
/*
* Timeout while processing startup packet.
* As for startup_die(), we clean up and exit(1).
*/
static void
StartupPacketTimeoutHandler(void)
{
proc_exit(1);
}
/* /*
* RandomSalt * RandomSalt
*/ */
......
...@@ -27,8 +27,9 @@ ...@@ -27,8 +27,9 @@
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/latch.h" #include "storage/latch.h"
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/proc.h" #include "storage/standby.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/timeout.h"
/* /*
...@@ -185,20 +186,12 @@ StartupProcessMain(void) ...@@ -185,20 +186,12 @@ StartupProcessMain(void)
/* /*
* Properly accept or ignore signals the postmaster might send us. * Properly accept or ignore signals the postmaster might send us.
*
* Note: ideally we'd not enable handle_standby_sig_alarm unless actually
* doing hot standby, but we don't know that yet. Rely on it to not do
* anything if it shouldn't.
*/ */
pqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */ pqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */
pqsignal(SIGINT, SIG_IGN); /* ignore query cancel */ pqsignal(SIGINT, SIG_IGN); /* ignore query cancel */
pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */
pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */
if (EnableHotStandby) InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless
* InHotStandby */
else
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, StartupProcSigUsr1Handler); pqsignal(SIGUSR1, StartupProcSigUsr1Handler);
pqsignal(SIGUSR2, StartupProcTriggerHandler); pqsignal(SIGUSR2, StartupProcTriggerHandler);
...@@ -212,11 +205,20 @@ StartupProcessMain(void) ...@@ -212,11 +205,20 @@ StartupProcessMain(void)
pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL);
/*
* Register timeouts needed for standby mode
*/
RegisterTimeout(STANDBY_DEADLOCK_TIMEOUT, StandbyDeadLockHandler);
RegisterTimeout(STANDBY_TIMEOUT, StandbyTimeoutHandler);
/* /*
* Unblock signals (they were blocked when the postmaster forked us) * Unblock signals (they were blocked when the postmaster forked us)
*/ */
PG_SETMASK(&UnBlockSig); PG_SETMASK(&UnBlockSig);
/*
* Do what we came for.
*/
StartupXLOG(); StartupXLOG();
/* /*
......
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/resowner.h" #include "utils/resowner.h"
#include "utils/timeout.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
...@@ -1345,7 +1346,7 @@ WalSndSignals(void) ...@@ -1345,7 +1346,7 @@ WalSndSignals(void)
pqsignal(SIGINT, SIG_IGN); /* not used */ pqsignal(SIGINT, SIG_IGN); /* not used */
pqsignal(SIGTERM, WalSndShutdownHandler); /* request shutdown */ pqsignal(SIGTERM, WalSndShutdownHandler); /* request shutdown */
pqsignal(SIGQUIT, WalSndQuickDieHandler); /* hard crash time */ pqsignal(SIGQUIT, WalSndQuickDieHandler); /* hard crash time */
pqsignal(SIGALRM, SIG_IGN); InitializeTimeouts(); /* establishes SIGALRM handler */
pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */ pqsignal(SIGUSR1, WalSndXLogSendHandler); /* request WAL sending */
pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and pqsignal(SIGUSR2, WalSndLastCycleHandler); /* request a last cycle and
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "storage/sinvaladt.h" #include "storage/sinvaladt.h"
#include "storage/standby.h" #include "storage/standby.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/timeout.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
/* User-settable GUC parameters */ /* User-settable GUC parameters */
...@@ -40,6 +41,7 @@ static List *RecoveryLockList; ...@@ -40,6 +41,7 @@ static List *RecoveryLockList;
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
ProcSignalReason reason); ProcSignalReason reason);
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid); static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts); static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks); static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
...@@ -370,13 +372,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid) ...@@ -370,13 +372,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
* ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup() * ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup()
* to resolve conflicts with other backends holding buffer pins. * to resolve conflicts with other backends holding buffer pins.
* *
* We either resolve conflicts immediately or set a SIGALRM to wake us at * The ProcWaitForSignal() sleep normally done in LockBufferForCleanup()
* the limit of our patience. The sleep in LockBufferForCleanup() is * (when not InHotStandby) is performed here, for code clarity.
* performed here, for code clarity. *
* We either resolve conflicts immediately or set a timeout to wake us at
* the limit of our patience.
* *
* Resolve conflicts by sending a PROCSIG signal to all backends to check if * Resolve conflicts by sending a PROCSIG signal to all backends to check if
* they hold one of the buffer pins that is blocking Startup process. If so, * they hold one of the buffer pins that is blocking Startup process. If so,
* backends will take an appropriate error action, ERROR or FATAL. * those backends will take an appropriate error action, ERROR or FATAL.
* *
* We also must check for deadlocks. Deadlocks occur because if queries * We also must check for deadlocks. Deadlocks occur because if queries
* wait on a lock, that must be behind an AccessExclusiveLock, which can only * wait on a lock, that must be behind an AccessExclusiveLock, which can only
...@@ -389,32 +393,26 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid) ...@@ -389,32 +393,26 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
* *
* Deadlocks are extremely rare, and relatively expensive to check for, * Deadlocks are extremely rare, and relatively expensive to check for,
* so we don't do a deadlock check right away ... only if we have had to wait * so we don't do a deadlock check right away ... only if we have had to wait
* at least deadlock_timeout. Most of the logic about that is in proc.c. * at least deadlock_timeout.
*/ */
void void
ResolveRecoveryConflictWithBufferPin(void) ResolveRecoveryConflictWithBufferPin(void)
{ {
bool sig_alarm_enabled = false;
TimestampTz ltime; TimestampTz ltime;
TimestampTz now;
Assert(InHotStandby); Assert(InHotStandby);
ltime = GetStandbyLimitTime(); ltime = GetStandbyLimitTime();
now = GetCurrentTimestamp();
if (!ltime) if (ltime == 0)
{ {
/* /*
* We're willing to wait forever for conflicts, so set timeout for * We're willing to wait forever for conflicts, so set timeout for
* deadlock check (only) * deadlock check only
*/ */
if (enable_standby_sig_alarm(now, now, true)) enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
sig_alarm_enabled = true;
else
elog(FATAL, "could not set timer for process wakeup");
} }
else if (now >= ltime) else if (GetCurrentTimestamp() >= ltime)
{ {
/* /*
* We're already behind, so clear a path as quickly as possible. * We're already behind, so clear a path as quickly as possible.
...@@ -427,23 +425,23 @@ ResolveRecoveryConflictWithBufferPin(void) ...@@ -427,23 +425,23 @@ ResolveRecoveryConflictWithBufferPin(void)
* Wake up at ltime, and check for deadlocks as well if we will be * Wake up at ltime, and check for deadlocks as well if we will be
* waiting longer than deadlock_timeout * waiting longer than deadlock_timeout
*/ */
if (enable_standby_sig_alarm(now, ltime, false)) enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
sig_alarm_enabled = true; enable_timeout_at(STANDBY_TIMEOUT, ltime);
else
elog(FATAL, "could not set timer for process wakeup");
} }
/* Wait to be signaled by UnpinBuffer() */ /* Wait to be signaled by UnpinBuffer() */
ProcWaitForSignal(); ProcWaitForSignal();
if (sig_alarm_enabled) /*
{ * Clear any timeout requests established above. We assume here that
if (!disable_standby_sig_alarm()) * the Startup process doesn't have any other timeouts than what this
elog(FATAL, "could not disable timer for process wakeup"); * function uses. If that stops being true, we could cancel the
} * timeouts individually, but that'd be slower.
*/
disable_all_timeouts(false);
} }
void static void
SendRecoveryConflictWithBufferPin(ProcSignalReason reason) SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
{ {
Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN || Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
...@@ -492,6 +490,38 @@ CheckRecoveryConflictDeadlock(void) ...@@ -492,6 +490,38 @@ CheckRecoveryConflictDeadlock(void)
errdetail("User transaction caused buffer deadlock with recovery."))); errdetail("User transaction caused buffer deadlock with recovery.")));
} }
/* --------------------------------
* timeout handler routines
* --------------------------------
*/
/*
* StandbyDeadLockHandler() will be called if STANDBY_DEADLOCK_TIMEOUT
* occurs before STANDBY_TIMEOUT. Send out a request for hot-standby
* backends to check themselves for deadlocks.
*/
void
StandbyDeadLockHandler(void)
{
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
}
/*
* StandbyTimeoutHandler() will be called if STANDBY_TIMEOUT is exceeded.
* Send out a request to release conflicting buffer pins unconditionally,
* so we can press ahead with applying changes in recovery.
*/
void
StandbyTimeoutHandler(void)
{
/* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
}
/* /*
* ----------------------------------------------------- * -----------------------------------------------------
* Locking in Recovery Mode * Locking in Recovery Mode
......
This diff is collapsed.
...@@ -72,6 +72,7 @@ ...@@ -72,6 +72,7 @@
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
#include "utils/timeout.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
...@@ -2396,9 +2397,9 @@ start_xact_command(void) ...@@ -2396,9 +2397,9 @@ start_xact_command(void)
/* Set statement timeout running, if any */ /* Set statement timeout running, if any */
/* NB: this mustn't be enabled until we are within an xact */ /* NB: this mustn't be enabled until we are within an xact */
if (StatementTimeout > 0) if (StatementTimeout > 0)
enable_sig_alarm(StatementTimeout, true); enable_timeout_after(STATEMENT_TIMEOUT, StatementTimeout);
else else
cancel_from_timeout = false; disable_timeout(STATEMENT_TIMEOUT, false);
xact_started = true; xact_started = true;
} }
...@@ -2410,7 +2411,7 @@ finish_xact_command(void) ...@@ -2410,7 +2411,7 @@ finish_xact_command(void)
if (xact_started) if (xact_started)
{ {
/* Cancel any active statement timeout before committing */ /* Cancel any active statement timeout before committing */
disable_sig_alarm(true); disable_timeout(STATEMENT_TIMEOUT, false);
/* Now commit the command */ /* Now commit the command */
ereport(DEBUG3, ereport(DEBUG3,
...@@ -2891,7 +2892,7 @@ ProcessInterrupts(void) ...@@ -2891,7 +2892,7 @@ ProcessInterrupts(void)
(errcode(ERRCODE_QUERY_CANCELED), (errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling authentication due to timeout"))); errmsg("canceling authentication due to timeout")));
} }
if (cancel_from_timeout) if (get_timeout_indicator(STATEMENT_TIMEOUT))
{ {
ImmediateInterruptOK = false; /* not idle anymore */ ImmediateInterruptOK = false; /* not idle anymore */
DisableNotifyInterrupt(); DisableNotifyInterrupt();
...@@ -3614,7 +3615,7 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -3614,7 +3615,7 @@ PostgresMain(int argc, char *argv[], const char *username)
pqsignal(SIGQUIT, quickdie); /* hard crash time */ pqsignal(SIGQUIT, quickdie); /* hard crash time */
else else
pqsignal(SIGQUIT, die); /* cancel current query and exit */ pqsignal(SIGQUIT, die); /* cancel current query and exit */
pqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */ InitializeTimeouts(); /* establishes SIGALRM handler */
/* /*
* Ignore failure to write to frontend. Note: if frontend closes * Ignore failure to write to frontend. Note: if frontend closes
...@@ -3802,10 +3803,10 @@ PostgresMain(int argc, char *argv[], const char *username) ...@@ -3802,10 +3803,10 @@ PostgresMain(int argc, char *argv[], const char *username)
/* /*
* Forget any pending QueryCancel request, since we're returning to * Forget any pending QueryCancel request, since we're returning to
* the idle loop anyway, and cancel the statement timer if running. * the idle loop anyway, and cancel any active timeout requests.
*/ */
QueryCancelPending = false; QueryCancelPending = false;
disable_sig_alarm(true); disable_all_timeouts(false);
QueryCancelPending = false; /* again in case timeout occurred */ QueryCancelPending = false; /* again in case timeout occurred */
/* /*
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/procsignal.h" #include "storage/procsignal.h"
#include "storage/proc.h" #include "storage/proc.h"
...@@ -56,6 +55,7 @@ ...@@ -56,6 +55,7 @@
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/timeout.h"
#include "utils/tqual.h" #include "utils/tqual.h"
...@@ -65,6 +65,7 @@ static void PerformAuthentication(Port *port); ...@@ -65,6 +65,7 @@ static void PerformAuthentication(Port *port);
static void CheckMyDatabase(const char *name, bool am_superuser); static void CheckMyDatabase(const char *name, bool am_superuser);
static void InitCommunication(void); static void InitCommunication(void);
static void ShutdownPostgres(int code, Datum arg); static void ShutdownPostgres(int code, Datum arg);
static void StatementTimeoutHandler(void);
static bool ThereIsAtLeastOneRole(void); static bool ThereIsAtLeastOneRole(void);
static void process_startup_options(Port *port, bool am_superuser); static void process_startup_options(Port *port, bool am_superuser);
static void process_settings(Oid databaseid, Oid roleid); static void process_settings(Oid databaseid, Oid roleid);
...@@ -205,8 +206,7 @@ PerformAuthentication(Port *port) ...@@ -205,8 +206,7 @@ PerformAuthentication(Port *port)
* during authentication. Since we're inside a transaction and might do * during authentication. Since we're inside a transaction and might do
* database access, we have to use the statement_timeout infrastructure. * database access, we have to use the statement_timeout infrastructure.
*/ */
if (!enable_sig_alarm(AuthenticationTimeout * 1000, true)) enable_timeout_after(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000);
elog(FATAL, "could not set timer for authorization timeout");
/* /*
* Now perform authentication exchange. * Now perform authentication exchange.
...@@ -216,8 +216,7 @@ PerformAuthentication(Port *port) ...@@ -216,8 +216,7 @@ PerformAuthentication(Port *port)
/* /*
* Done with authentication. Disable the timeout, and log if needed. * Done with authentication. Disable the timeout, and log if needed.
*/ */
if (!disable_sig_alarm(true)) disable_timeout(STATEMENT_TIMEOUT, false);
elog(FATAL, "could not disable timer for authorization timeout");
if (Log_connections) if (Log_connections)
{ {
...@@ -495,6 +494,16 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, ...@@ -495,6 +494,16 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
/* Now that we have a BackendId, we can participate in ProcSignal */ /* Now that we have a BackendId, we can participate in ProcSignal */
ProcSignalInit(MyBackendId); ProcSignalInit(MyBackendId);
/*
* Also set up timeout handlers needed for backend operation. We need
* these in every case except bootstrap.
*/
if (!bootstrap)
{
RegisterTimeout(DEADLOCK_TIMEOUT, CheckDeadLock);
RegisterTimeout(STATEMENT_TIMEOUT, StatementTimeoutHandler);
}
/* /*
* bufmgr needs another initialization call too * bufmgr needs another initialization call too
*/ */
...@@ -974,6 +983,20 @@ ShutdownPostgres(int code, Datum arg) ...@@ -974,6 +983,20 @@ ShutdownPostgres(int code, Datum arg)
} }
/*
* STATEMENT_TIMEOUT handler: trigger a query-cancel interrupt.
*/
static void
StatementTimeoutHandler(void)
{
#ifdef HAVE_SETSID
/* try to signal whole process group */
kill(-MyProcPid, SIGINT);
#endif
kill(MyProcPid, SIGINT);
}
/* /*
* Returns true if at least one role is defined in this database cluster. * Returns true if at least one role is defined in this database cluster.
*/ */
......
...@@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global ...@@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \ OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \
rbtree.o superuser.o timeout.o tzparser.o
# This location might depend on the installation directories. Therefore # This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h. # we can't subsitute it into pg_config.h.
......
This diff is collapsed.
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#define _PROC_H_ #define _PROC_H_
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
#include "datatype/timestamp.h"
#include "storage/latch.h" #include "storage/latch.h"
#include "storage/lock.h" #include "storage/lock.h"
#include "storage/pg_sema.h" #include "storage/pg_sema.h"
...@@ -222,8 +221,6 @@ extern int DeadlockTimeout; ...@@ -222,8 +221,6 @@ extern int DeadlockTimeout;
extern int StatementTimeout; extern int StatementTimeout;
extern bool log_lock_waits; extern bool log_lock_waits;
extern volatile bool cancel_from_timeout;
/* /*
* Function Prototypes * Function Prototypes
...@@ -246,19 +243,11 @@ extern void ProcQueueInit(PROC_QUEUE *queue); ...@@ -246,19 +243,11 @@ extern void ProcQueueInit(PROC_QUEUE *queue);
extern int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable); extern int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus); extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock); extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
extern void CheckDeadLock(void);
extern bool IsWaitingForLock(void); extern bool IsWaitingForLock(void);
extern void LockErrorCleanup(void); extern void LockErrorCleanup(void);
extern void ProcWaitForSignal(void); extern void ProcWaitForSignal(void);
extern void ProcSendSignal(int pid); extern void ProcSendSignal(int pid);
extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
extern bool disable_sig_alarm(bool is_statement_timeout);
extern void handle_sig_alarm(SIGNAL_ARGS);
extern bool enable_standby_sig_alarm(TimestampTz now,
TimestampTz fin_time, bool deadlock_only);
extern bool disable_standby_sig_alarm(void);
extern void handle_standby_sig_alarm(SIGNAL_ARGS);
#endif /* PROC_H */ #endif /* PROC_H */
...@@ -33,8 +33,9 @@ extern void ResolveRecoveryConflictWithTablespace(Oid tsid); ...@@ -33,8 +33,9 @@ extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
extern void ResolveRecoveryConflictWithDatabase(Oid dbid); extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
extern void ResolveRecoveryConflictWithBufferPin(void); extern void ResolveRecoveryConflictWithBufferPin(void);
extern void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
extern void CheckRecoveryConflictDeadlock(void); extern void CheckRecoveryConflictDeadlock(void);
extern void StandbyDeadLockHandler(void);
extern void StandbyTimeoutHandler(void);
/* /*
* Standby Rmgr (RM_STANDBY_ID) * Standby Rmgr (RM_STANDBY_ID)
......
/*-------------------------------------------------------------------------
*
* timeout.h
* Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
*
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/timeout.h
*
*-------------------------------------------------------------------------
*/
#ifndef TIMEOUT_H
#define TIMEOUT_H
#include "datatype/timestamp.h"
/*
* Identifiers for timeout reasons. Note that in case multiple timeouts
* trigger at the same time, they are serviced in the order of this enum.
*/
typedef enum TimeoutId
{
/* Predefined timeout reasons */
STARTUP_PACKET_TIMEOUT,
DEADLOCK_TIMEOUT,
STATEMENT_TIMEOUT,
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
/* First user-definable timeout reason */
USER_TIMEOUT,
/* Maximum number of timeout reasons */
MAX_TIMEOUTS = 16
} TimeoutId;
/* callback function signature */
typedef void (*timeout_handler) (void);
/* timeout setup */
extern void InitializeTimeouts(void);
extern TimeoutId RegisterTimeout(TimeoutId id, timeout_handler handler);
/* timeout operation */
extern void enable_timeout_after(TimeoutId id, int delay_ms);
extern void enable_timeout_at(TimeoutId id, TimestampTz fin_time);
extern void disable_timeout(TimeoutId id, bool keep_indicator);
extern void disable_all_timeouts(bool keep_indicators);
/* accessors */
extern bool get_timeout_indicator(TimeoutId id);
extern TimestampTz get_timeout_start_time(TimeoutId id);
#endif /* TIMEOUT_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment