diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index dade5cc3c051c855de2d2800a4db74f0907e41bb..7c946804a5fddcd3dc905a0f0fcefc788f6ead86 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -97,6 +97,7 @@
 #include "utils/rel.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 #include "utils/tqual.h"
 
@@ -432,7 +433,7 @@ AutoVacLauncherMain(int argc, char *argv[])
 	pqsignal(SIGTERM, avl_sigterm_handler);
 
 	pqsignal(SIGQUIT, quickdie);
-	pqsignal(SIGALRM, handle_sig_alarm);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
 
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
@@ -482,9 +483,9 @@ AutoVacLauncherMain(int argc, char *argv[])
 		/* Prevents interrupts while cleaning up */
 		HOLD_INTERRUPTS();
 
-		/* Forget any pending QueryCancel request */
+		/* Forget any pending QueryCancel or timeout request */
 		QueryCancelPending = false;
-		disable_sig_alarm(true);
+		disable_all_timeouts(false);
 		QueryCancelPending = false;		/* again in case timeout occurred */
 
 		/* Report the error to the server log */
@@ -1492,7 +1493,7 @@ AutoVacWorkerMain(int argc, char *argv[])
 	pqsignal(SIGINT, StatementCancelHandler);
 	pqsignal(SIGTERM, die);
 	pqsignal(SIGQUIT, quickdie);
-	pqsignal(SIGALRM, handle_sig_alarm);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
 
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, procsignal_sigusr1_handler);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 45f6ac624eb09e6878403cfdc0f1b5e0e05bf389..0be3230c2a5cbf5b697c4ebf6ff98578805e140d 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -112,12 +112,12 @@
 #include "storage/ipc.h"
 #include "storage/pg_shmem.h"
 #include "storage/pmsignal.h"
-#include "storage/proc.h"
 #include "tcop/tcopprot.h"
 #include "utils/builtins.h"
 #include "utils/datetime.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
+#include "utils/timeout.h"
 
 #ifdef EXEC_BACKEND
 #include "storage/spin.h"
@@ -337,6 +337,7 @@ static void reaper(SIGNAL_ARGS);
 static void sigusr1_handler(SIGNAL_ARGS);
 static void startup_die(SIGNAL_ARGS);
 static void dummy_handler(SIGNAL_ARGS);
+static void StartupPacketTimeoutHandler(void);
 static void CleanupBackend(int pid, int exitstatus);
 static void HandleChildCrash(int pid, int exitstatus, const char *procname);
 static void LogChildExit(int lev, const char *procname,
@@ -3415,7 +3416,7 @@ BackendInitialize(Port *port)
 	 */
 	pqsignal(SIGTERM, startup_die);
 	pqsignal(SIGQUIT, startup_die);
-	pqsignal(SIGALRM, startup_die);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
 	PG_SETMASK(&StartupBlockSig);
 
 	/*
@@ -3469,9 +3470,18 @@ BackendInitialize(Port *port)
 	 * time delay, so that a broken client can't hog a connection
 	 * indefinitely.  PreAuthDelay and any DNS interactions above don't count
 	 * 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))
-		elog(FATAL, "could not set timer for startup packet timeout");
+	RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
+	enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
 
 	/*
 	 * Receive the startup packet (which might turn out to be a cancel request
@@ -3508,8 +3518,7 @@ BackendInitialize(Port *port)
 	/*
 	 * Disable the timeout, and prevent SIGTERM/SIGQUIT again.
 	 */
-	if (!disable_sig_alarm(false))
-		elog(FATAL, "could not disable timer for startup packet timeout");
+	disable_timeout(STARTUP_PACKET_TIMEOUT, false);
 	PG_SETMASK(&BlockSig);
 }
 
@@ -4311,8 +4320,8 @@ sigusr1_handler(SIGNAL_ARGS)
 }
 
 /*
- * Timeout or shutdown signal from postmaster while processing startup packet.
- * Cleanup and exit(1).
+ * SIGTERM or SIGQUIT while processing startup packet.
+ * Clean up and exit(1).
  *
  * XXX: possible future improvement: try to send a message indicating
  * why we are disconnecting.  Problem is to be sure we don't block while
@@ -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
  */
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index ed75d0958e077905c8bed74b8cde01ef2551d787..ab4d1645f246aa9c56e14bf0374e69dfb4a950c3 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -27,8 +27,9 @@
 #include "storage/ipc.h"
 #include "storage/latch.h"
 #include "storage/pmsignal.h"
-#include "storage/proc.h"
+#include "storage/standby.h"
 #include "utils/guc.h"
+#include "utils/timeout.h"
 
 
 /*
@@ -185,20 +186,12 @@ StartupProcessMain(void)
 
 	/*
 	 * 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(SIGINT, SIG_IGN);	/* ignore query cancel */
 	pqsignal(SIGTERM, StartupProcShutdownHandler);		/* request shutdown */
 	pqsignal(SIGQUIT, startupproc_quickdie);	/* hard crash time */
-	if (EnableHotStandby)
-		pqsignal(SIGALRM, handle_standby_sig_alarm);	/* ignored unless
-														 * InHotStandby */
-	else
-		pqsignal(SIGALRM, SIG_IGN);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, StartupProcSigUsr1Handler);
 	pqsignal(SIGUSR2, StartupProcTriggerHandler);
@@ -212,11 +205,20 @@ StartupProcessMain(void)
 	pqsignal(SIGCONT, 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)
 	 */
 	PG_SETMASK(&UnBlockSig);
 
+	/*
+	 * Do what we came for.
+	 */
 	StartupXLOG();
 
 	/*
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index d007f5f6fa1865cdd7be4446eecc60c33ebe1430..37a030b5f5e4e1536c44227672be05ffcff88537 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -63,6 +63,7 @@
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/resowner.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 
 
@@ -1345,7 +1346,7 @@ WalSndSignals(void)
 	pqsignal(SIGINT, SIG_IGN);	/* not used */
 	pqsignal(SIGTERM, WalSndShutdownHandler);	/* request shutdown */
 	pqsignal(SIGQUIT, WalSndQuickDieHandler);	/* hard crash time */
-	pqsignal(SIGALRM, SIG_IGN);
+	InitializeTimeouts();		/* establishes SIGALRM handler */
 	pqsignal(SIGPIPE, SIG_IGN);
 	pqsignal(SIGUSR1, WalSndXLogSendHandler);	/* request WAL sending */
 	pqsignal(SIGUSR2, WalSndLastCycleHandler);	/* request a last cycle and
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 995b68aae553f6cbbc1dbfc0a05ddeb4404bfc7c..43f74112733772089a33e39b623572b916fe2d35 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -28,6 +28,7 @@
 #include "storage/sinvaladt.h"
 #include "storage/standby.h"
 #include "utils/ps_status.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 
 /* User-settable GUC parameters */
@@ -40,6 +41,7 @@ static List *RecoveryLockList;
 static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
 									   ProcSignalReason reason);
 static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
+static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
 static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
 static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
 
@@ -370,13 +372,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
  * ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup()
  * to resolve conflicts with other backends holding buffer pins.
  *
- * We either resolve conflicts immediately or set a SIGALRM to wake us at
- * the limit of our patience. The sleep in LockBufferForCleanup() is
- * performed here, for code clarity.
+ * The ProcWaitForSignal() sleep normally done in LockBufferForCleanup()
+ * (when not InHotStandby) is 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
  * 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
  * wait on a lock, that must be behind an AccessExclusiveLock, which can only
@@ -389,32 +393,26 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
  *
  * 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
- * at least deadlock_timeout.  Most of the logic about that is in proc.c.
+ * at least deadlock_timeout.
  */
 void
 ResolveRecoveryConflictWithBufferPin(void)
 {
-	bool		sig_alarm_enabled = false;
 	TimestampTz ltime;
-	TimestampTz now;
 
 	Assert(InHotStandby);
 
 	ltime = GetStandbyLimitTime();
-	now = GetCurrentTimestamp();
 
-	if (!ltime)
+	if (ltime == 0)
 	{
 		/*
 		 * 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))
-			sig_alarm_enabled = true;
-		else
-			elog(FATAL, "could not set timer for process wakeup");
+		enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
 	}
-	else if (now >= ltime)
+	else if (GetCurrentTimestamp() >= ltime)
 	{
 		/*
 		 * We're already behind, so clear a path as quickly as possible.
@@ -427,23 +425,23 @@ ResolveRecoveryConflictWithBufferPin(void)
 		 * Wake up at ltime, and check for deadlocks as well if we will be
 		 * waiting longer than deadlock_timeout
 		 */
-		if (enable_standby_sig_alarm(now, ltime, false))
-			sig_alarm_enabled = true;
-		else
-			elog(FATAL, "could not set timer for process wakeup");
+		enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
+		enable_timeout_at(STANDBY_TIMEOUT, ltime);
 	}
 
 	/* Wait to be signaled by UnpinBuffer() */
 	ProcWaitForSignal();
 
-	if (sig_alarm_enabled)
-	{
-		if (!disable_standby_sig_alarm())
-			elog(FATAL, "could not disable timer for process wakeup");
-	}
+	/*
+	 * Clear any timeout requests established above.  We assume here that
+	 * the Startup process doesn't have any other timeouts than what this
+	 * 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)
 {
 	Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
@@ -492,6 +490,38 @@ CheckRecoveryConflictDeadlock(void)
 	   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
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index 21598d3c18635acee2728bd54910732b34904952..0df562b30640f35ebd27e43389c0b99ecf3d87be 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -48,6 +48,7 @@
 #include "storage/procarray.h"
 #include "storage/procsignal.h"
 #include "storage/spin.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 
 
@@ -77,26 +78,13 @@ PGPROC	   *PreparedXactProcs = NULL;
 /* If we are waiting for a lock, this points to the associated LOCALLOCK */
 static LOCALLOCK *lockAwaited = NULL;
 
-/* Mark these volatile because they can be changed by signal handler */
-static volatile bool standby_timeout_active = false;
-static volatile bool statement_timeout_active = false;
-static volatile bool deadlock_timeout_active = false;
+/* Mark this volatile because it can be changed by signal handler */
 static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED;
-volatile bool cancel_from_timeout = false;
-
-/* timeout_start_time is set when log_lock_waits is true */
-static TimestampTz timeout_start_time;
-
-/* statement_fin_time is valid only if statement_timeout_active is true */
-static TimestampTz statement_fin_time;
-static TimestampTz statement_fin_time2; /* valid only in recovery */
 
 
 static void RemoveProcFromArray(int code, Datum arg);
 static void ProcKill(int code, Datum arg);
 static void AuxiliaryProcKill(int code, Datum arg);
-static bool CheckStatementTimeout(void);
-static bool CheckStandbyTimeout(void);
 
 
 /*
@@ -653,7 +641,7 @@ LockErrorCleanup(void)
 		return;
 
 	/* Turn off the deadlock timer, if it's still running (see ProcSleep) */
-	disable_sig_alarm(false);
+	disable_timeout(DEADLOCK_TIMEOUT, false);
 
 	/* Unlink myself from the wait queue, if on it (might not be anymore!) */
 	partitionLock = LockHashPartitionLock(lockAwaited->hashcode);
@@ -1036,7 +1024,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	if (RecoveryInProgress() && !InRecovery)
 		CheckRecoveryConflictDeadlock();
 
-	/* Reset deadlock_state before enabling the signal handler */
+	/* Reset deadlock_state before enabling the timeout handler */
 	deadlock_state = DS_NOT_YET_CHECKED;
 
 	/*
@@ -1048,8 +1036,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	 * By delaying the check until we've waited for a bit, we can avoid
 	 * running the rather expensive deadlock-check code in most cases.
 	 */
-	if (!enable_sig_alarm(DeadlockTimeout, false))
-		elog(FATAL, "could not set timer for process wakeup");
+	enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
 
 	/*
 	 * If someone wakes us between LWLockRelease and PGSemaphoreLock,
@@ -1065,8 +1052,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	 * that we don't mind losing control to a cancel/die interrupt here.  We
 	 * don't, because we have no shared-state-change work to do after being
 	 * granted the lock (the grantor did it all).  We do have to worry about
-	 * updating the locallock table, but if we lose control to an error,
-	 * LockErrorCleanup will fix that up.
+	 * canceling the deadlock timeout and updating the locallock table, but if
+	 * we lose control to an error, LockErrorCleanup will fix that up.
 	 */
 	do
 	{
@@ -1138,7 +1125,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 			DescribeLockTag(&buf, &locallock->tag.lock);
 			modename = GetLockmodeName(locallock->tag.lock.locktag_lockmethodid,
 									   lockmode);
-			TimestampDifference(timeout_start_time, GetCurrentTimestamp(),
+			TimestampDifference(get_timeout_start_time(DEADLOCK_TIMEOUT),
+								GetCurrentTimestamp(),
 								&secs, &usecs);
 			msecs = secs * 1000 + usecs / 1000;
 			usecs = usecs % 1000;
@@ -1200,8 +1188,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 	/*
 	 * Disable the timer, if it's still running
 	 */
-	if (!disable_sig_alarm(false))
-		elog(FATAL, "could not disable timer for process wakeup");
+	disable_timeout(DEADLOCK_TIMEOUT, false);
 
 	/*
 	 * Re-acquire the lock table's partition lock.  We have to do this to hold
@@ -1334,7 +1321,7 @@ ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock)
 /*
  * CheckDeadLock
  *
- * We only get to this routine if we got SIGALRM after DeadlockTimeout
+ * We only get to this routine if the DEADLOCK_TIMEOUT fired
  * while waiting for a lock to be released by some other process.  Look
  * to see if there's a deadlock; if not, just return and continue waiting.
  * (But signal ProcSleep to log a message, if log_lock_waits is true.)
@@ -1344,7 +1331,7 @@ ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock)
  * NB: this is run inside a signal handler, so be very wary about what is done
  * here or in called routines.
  */
-static void
+void
 CheckDeadLock(void)
 {
 	int			i;
@@ -1498,401 +1485,3 @@ ProcSendSignal(int pid)
 	if (proc != NULL)
 		PGSemaphoreUnlock(&proc->sem);
 }
-
-
-/*****************************************************************************
- * SIGALRM interrupt support
- *
- * Maybe these should be in pqsignal.c?
- *****************************************************************************/
-
-/*
- * Enable the SIGALRM interrupt to fire after the specified delay
- *
- * Delay is given in milliseconds.	Caller should be sure a SIGALRM
- * signal handler is installed before this is called.
- *
- * This code properly handles nesting of deadlock timeout alarms within
- * statement timeout alarms.
- *
- * Returns TRUE if okay, FALSE on failure.
- */
-bool
-enable_sig_alarm(int delayms, bool is_statement_timeout)
-{
-	TimestampTz fin_time;
-	struct itimerval timeval;
-
-	if (is_statement_timeout)
-	{
-		/*
-		 * Begin statement-level timeout
-		 *
-		 * Note that we compute statement_fin_time with reference to the
-		 * statement_timestamp, but apply the specified delay without any
-		 * correction; that is, we ignore whatever time has elapsed since
-		 * statement_timestamp was set.  In the normal case only a small
-		 * interval will have elapsed and so this doesn't matter, but there
-		 * are corner cases (involving multi-statement query strings with
-		 * embedded COMMIT or ROLLBACK) where we might re-initialize the
-		 * statement timeout long after initial receipt of the message. In
-		 * such cases the enforcement of the statement timeout will be a bit
-		 * inconsistent.  This annoyance is judged not worth the cost of
-		 * performing an additional gettimeofday() here.
-		 */
-		Assert(!deadlock_timeout_active);
-		fin_time = GetCurrentStatementStartTimestamp();
-		fin_time = TimestampTzPlusMilliseconds(fin_time, delayms);
-		statement_fin_time = fin_time;
-		cancel_from_timeout = false;
-		statement_timeout_active = true;
-	}
-	else if (statement_timeout_active)
-	{
-		/*
-		 * Begin deadlock timeout with statement-level timeout active
-		 *
-		 * Here, we want to interrupt at the closer of the two timeout times.
-		 * If fin_time >= statement_fin_time then we need not touch the
-		 * existing timer setting; else set up to interrupt at the deadlock
-		 * timeout time.
-		 *
-		 * NOTE: in this case it is possible that this routine will be
-		 * interrupted by the previously-set timer alarm.  This is okay
-		 * because the signal handler will do only what it should do according
-		 * to the state variables.	The deadlock checker may get run earlier
-		 * than normal, but that does no harm.
-		 */
-		timeout_start_time = GetCurrentTimestamp();
-		fin_time = TimestampTzPlusMilliseconds(timeout_start_time, delayms);
-		deadlock_timeout_active = true;
-		if (fin_time >= statement_fin_time)
-			return true;
-	}
-	else
-	{
-		/* Begin deadlock timeout with no statement-level timeout */
-		deadlock_timeout_active = true;
-		/* GetCurrentTimestamp can be expensive, so only do it if we must */
-		if (log_lock_waits)
-			timeout_start_time = GetCurrentTimestamp();
-	}
-
-	/* If we reach here, okay to set the timer interrupt */
-	MemSet(&timeval, 0, sizeof(struct itimerval));
-	timeval.it_value.tv_sec = delayms / 1000;
-	timeval.it_value.tv_usec = (delayms % 1000) * 1000;
-	if (setitimer(ITIMER_REAL, &timeval, NULL))
-		return false;
-	return true;
-}
-
-/*
- * Cancel the SIGALRM timer, either for a deadlock timeout or a statement
- * timeout.  If a deadlock timeout is canceled, any active statement timeout
- * remains in force.
- *
- * Returns TRUE if okay, FALSE on failure.
- */
-bool
-disable_sig_alarm(bool is_statement_timeout)
-{
-	/*
-	 * Always disable the interrupt if it is active; this avoids being
-	 * interrupted by the signal handler and thereby possibly getting
-	 * confused.
-	 *
-	 * We will re-enable the interrupt if necessary in CheckStatementTimeout.
-	 */
-	if (statement_timeout_active || deadlock_timeout_active)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL))
-		{
-			statement_timeout_active = false;
-			cancel_from_timeout = false;
-			deadlock_timeout_active = false;
-			return false;
-		}
-	}
-
-	/* Always cancel deadlock timeout, in case this is error cleanup */
-	deadlock_timeout_active = false;
-
-	/* Cancel or reschedule statement timeout */
-	if (is_statement_timeout)
-	{
-		statement_timeout_active = false;
-		cancel_from_timeout = false;
-	}
-	else if (statement_timeout_active)
-	{
-		if (!CheckStatementTimeout())
-			return false;
-	}
-	return true;
-}
-
-
-/*
- * Check for statement timeout.  If the timeout time has come,
- * trigger a query-cancel interrupt; if not, reschedule the SIGALRM
- * interrupt to occur at the right time.
- *
- * Returns true if okay, false if failed to set the interrupt.
- */
-static bool
-CheckStatementTimeout(void)
-{
-	TimestampTz now;
-
-	if (!statement_timeout_active)
-		return true;			/* do nothing if not active */
-
-	now = GetCurrentTimestamp();
-
-	if (now >= statement_fin_time)
-	{
-		/* Time to die */
-		statement_timeout_active = false;
-		cancel_from_timeout = true;
-#ifdef HAVE_SETSID
-		/* try to signal whole process group */
-		kill(-MyProcPid, SIGINT);
-#endif
-		kill(MyProcPid, SIGINT);
-	}
-	else
-	{
-		/* Not time yet, so (re)schedule the interrupt */
-		long		secs;
-		int			usecs;
-		struct itimerval timeval;
-
-		TimestampDifference(now, statement_fin_time,
-							&secs, &usecs);
-
-		/*
-		 * It's possible that the difference is less than a microsecond;
-		 * ensure we don't cancel, rather than set, the interrupt.
-		 */
-		if (secs == 0 && usecs == 0)
-			usecs = 1;
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		timeval.it_value.tv_sec = secs;
-		timeval.it_value.tv_usec = usecs;
-		if (setitimer(ITIMER_REAL, &timeval, NULL))
-			return false;
-	}
-
-	return true;
-}
-
-
-/*
- * Signal handler for SIGALRM for normal user backends
- *
- * Process deadlock check and/or statement timeout check, as needed.
- * To avoid various edge cases, we must be careful to do nothing
- * when there is nothing to be done.  We also need to be able to
- * reschedule the timer interrupt if called before end of statement.
- */
-void
-handle_sig_alarm(SIGNAL_ARGS)
-{
-	int			save_errno = errno;
-
-	/* SIGALRM is cause for waking anything waiting on the process latch */
-	if (MyProc)
-		SetLatch(&MyProc->procLatch);
-
-	if (deadlock_timeout_active)
-	{
-		deadlock_timeout_active = false;
-		CheckDeadLock();
-	}
-
-	if (statement_timeout_active)
-		(void) CheckStatementTimeout();
-
-	errno = save_errno;
-}
-
-/*
- * Signal handler for SIGALRM in Startup process
- *
- * To avoid various edge cases, we must be careful to do nothing
- * when there is nothing to be done.  We also need to be able to
- * reschedule the timer interrupt if called before end of statement.
- *
- * We set either deadlock_timeout_active or statement_timeout_active
- * or both. Interrupts are enabled if standby_timeout_active.
- */
-bool
-enable_standby_sig_alarm(TimestampTz now, TimestampTz fin_time, bool deadlock_only)
-{
-	TimestampTz deadlock_time = TimestampTzPlusMilliseconds(now,
-															DeadlockTimeout);
-
-	if (deadlock_only)
-	{
-		/*
-		 * Wake up at deadlock_time only, then wait forever
-		 */
-		statement_fin_time = deadlock_time;
-		deadlock_timeout_active = true;
-		statement_timeout_active = false;
-	}
-	else if (fin_time > deadlock_time)
-	{
-		/*
-		 * Wake up at deadlock_time, then again at fin_time
-		 */
-		statement_fin_time = deadlock_time;
-		statement_fin_time2 = fin_time;
-		deadlock_timeout_active = true;
-		statement_timeout_active = true;
-	}
-	else
-	{
-		/*
-		 * Wake only at fin_time because its fairly soon
-		 */
-		statement_fin_time = fin_time;
-		deadlock_timeout_active = false;
-		statement_timeout_active = true;
-	}
-
-	if (deadlock_timeout_active || statement_timeout_active)
-	{
-		long		secs;
-		int			usecs;
-		struct itimerval timeval;
-
-		TimestampDifference(now, statement_fin_time,
-							&secs, &usecs);
-		if (secs == 0 && usecs == 0)
-			usecs = 1;
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		timeval.it_value.tv_sec = secs;
-		timeval.it_value.tv_usec = usecs;
-		if (setitimer(ITIMER_REAL, &timeval, NULL))
-			return false;
-		standby_timeout_active = true;
-	}
-
-	return true;
-}
-
-bool
-disable_standby_sig_alarm(void)
-{
-	/*
-	 * Always disable the interrupt if it is active; this avoids being
-	 * interrupted by the signal handler and thereby possibly getting
-	 * confused.
-	 *
-	 * We will re-enable the interrupt if necessary in CheckStandbyTimeout.
-	 */
-	if (standby_timeout_active)
-	{
-		struct itimerval timeval;
-
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		if (setitimer(ITIMER_REAL, &timeval, NULL))
-		{
-			standby_timeout_active = false;
-			return false;
-		}
-	}
-
-	standby_timeout_active = false;
-
-	return true;
-}
-
-/*
- * CheckStandbyTimeout() runs unconditionally in the Startup process
- * SIGALRM handler. Timers will only be set when InHotStandby.
- * We simply ignore any signals unless the timer has been set.
- */
-static bool
-CheckStandbyTimeout(void)
-{
-	TimestampTz now;
-	bool		reschedule = false;
-
-	standby_timeout_active = false;
-
-	now = GetCurrentTimestamp();
-
-	/*
-	 * Reschedule the timer if its not time to wake yet, or if we have both
-	 * timers set and the first one has just been reached.
-	 */
-	if (now >= statement_fin_time)
-	{
-		if (deadlock_timeout_active)
-		{
-			/*
-			 * We're still waiting when we reach deadlock timeout, so send out
-			 * a request to have other backends check themselves for deadlock.
-			 * Then continue waiting until statement_fin_time, if that's set.
-			 */
-			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
-			deadlock_timeout_active = false;
-
-			/*
-			 * Begin second waiting period if required.
-			 */
-			if (statement_timeout_active)
-			{
-				reschedule = true;
-				statement_fin_time = statement_fin_time2;
-			}
-		}
-		else
-		{
-			/*
-			 * We've now reached statement_fin_time, so ask all conflicts to
-			 * leave, so we can press ahead with applying changes in recovery.
-			 */
-			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
-		}
-	}
-	else
-		reschedule = true;
-
-	if (reschedule)
-	{
-		long		secs;
-		int			usecs;
-		struct itimerval timeval;
-
-		TimestampDifference(now, statement_fin_time,
-							&secs, &usecs);
-		if (secs == 0 && usecs == 0)
-			usecs = 1;
-		MemSet(&timeval, 0, sizeof(struct itimerval));
-		timeval.it_value.tv_sec = secs;
-		timeval.it_value.tv_usec = usecs;
-		if (setitimer(ITIMER_REAL, &timeval, NULL))
-			return false;
-		standby_timeout_active = true;
-	}
-
-	return true;
-}
-
-void
-handle_standby_sig_alarm(SIGNAL_ARGS)
-{
-	int			save_errno = errno;
-
-	if (standby_timeout_active)
-		(void) CheckStandbyTimeout();
-
-	errno = save_errno;
-}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f696375cabc5d5e2ddbd3689aaebc13616fddac8..37dfa18c1d0b793d096f662597b4b0a2687a1fd6 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -72,6 +72,7 @@
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
 #include "utils/snapmgr.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 #include "mb/pg_wchar.h"
 
@@ -2396,9 +2397,9 @@ start_xact_command(void)
 		/* Set statement timeout running, if any */
 		/* NB: this mustn't be enabled until we are within an xact */
 		if (StatementTimeout > 0)
-			enable_sig_alarm(StatementTimeout, true);
+			enable_timeout_after(STATEMENT_TIMEOUT, StatementTimeout);
 		else
-			cancel_from_timeout = false;
+			disable_timeout(STATEMENT_TIMEOUT, false);
 
 		xact_started = true;
 	}
@@ -2410,7 +2411,7 @@ finish_xact_command(void)
 	if (xact_started)
 	{
 		/* Cancel any active statement timeout before committing */
-		disable_sig_alarm(true);
+		disable_timeout(STATEMENT_TIMEOUT, false);
 
 		/* Now commit the command */
 		ereport(DEBUG3,
@@ -2891,7 +2892,7 @@ ProcessInterrupts(void)
 					(errcode(ERRCODE_QUERY_CANCELED),
 					 errmsg("canceling authentication due to timeout")));
 		}
-		if (cancel_from_timeout)
+		if (get_timeout_indicator(STATEMENT_TIMEOUT))
 		{
 			ImmediateInterruptOK = false;		/* not idle anymore */
 			DisableNotifyInterrupt();
@@ -3614,7 +3615,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 			pqsignal(SIGQUIT, quickdie);		/* hard crash time */
 		else
 			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
@@ -3802,10 +3803,10 @@ PostgresMain(int argc, char *argv[], const char *username)
 
 		/*
 		 * 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;
-		disable_sig_alarm(true);
+		disable_all_timeouts(false);
 		QueryCancelPending = false;		/* again in case timeout occurred */
 
 		/*
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 4d4a895657ed8c67707340efa662c89a25df226f..6a3fc6f693f66304a487032e2099d1660590d836 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -41,7 +41,6 @@
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/lmgr.h"
-#include "storage/proc.h"
 #include "storage/procarray.h"
 #include "storage/procsignal.h"
 #include "storage/proc.h"
@@ -56,6 +55,7 @@
 #include "utils/ps_status.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
+#include "utils/timeout.h"
 #include "utils/tqual.h"
 
 
@@ -65,6 +65,7 @@ static void PerformAuthentication(Port *port);
 static void CheckMyDatabase(const char *name, bool am_superuser);
 static void InitCommunication(void);
 static void ShutdownPostgres(int code, Datum arg);
+static void StatementTimeoutHandler(void);
 static bool ThereIsAtLeastOneRole(void);
 static void process_startup_options(Port *port, bool am_superuser);
 static void process_settings(Oid databaseid, Oid roleid);
@@ -205,8 +206,7 @@ PerformAuthentication(Port *port)
 	 * during authentication.  Since we're inside a transaction and might do
 	 * database access, we have to use the statement_timeout infrastructure.
 	 */
-	if (!enable_sig_alarm(AuthenticationTimeout * 1000, true))
-		elog(FATAL, "could not set timer for authorization timeout");
+	enable_timeout_after(STATEMENT_TIMEOUT, AuthenticationTimeout * 1000);
 
 	/*
 	 * Now perform authentication exchange.
@@ -216,8 +216,7 @@ PerformAuthentication(Port *port)
 	/*
 	 * Done with authentication.  Disable the timeout, and log if needed.
 	 */
-	if (!disable_sig_alarm(true))
-		elog(FATAL, "could not disable timer for authorization timeout");
+	disable_timeout(STATEMENT_TIMEOUT, false);
 
 	if (Log_connections)
 	{
@@ -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 */
 	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
 	 */
@@ -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.
  */
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index cd9ba5d1cc235b2e3d8021ce75f9ec1ba5a106c7..08be3bd699d9aa450b08e2eacff2ac084178ba0b 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -14,8 +14,8 @@ include $(top_builddir)/src/Makefile.global
 
 override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
-OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
-       rbtree.o
+OBJS = guc.o help_config.o pg_rusage.o ps_status.o rbtree.o \
+       superuser.o timeout.o tzparser.o
 
 # This location might depend on the installation directories. Therefore
 # we can't subsitute it into pg_config.h.
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
new file mode 100644
index 0000000000000000000000000000000000000000..668d5f3b590dfdb37f7b3295da2186a4f5e655a5
--- /dev/null
+++ b/src/backend/utils/misc/timeout.c
@@ -0,0 +1,479 @@
+/*-------------------------------------------------------------------------
+ *
+ * timeout.c
+ *	  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
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/utils/misc/timeout.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "libpq/pqsignal.h"
+#include "storage/proc.h"
+#include "utils/timeout.h"
+#include "utils/timestamp.h"
+
+
+/* Data about any one timeout reason */
+typedef struct timeout_params
+{
+	TimeoutId	index;			/* identifier of timeout reason */
+
+	/* volatile because it may be changed from the signal handler */
+	volatile bool indicator;	/* true if timeout has occurred */
+
+	/* callback function for timeout, or NULL if timeout not registered */
+	timeout_handler timeout_handler;
+
+	TimestampTz start_time;		/* time that timeout was last activated */
+	TimestampTz fin_time;		/* if active, time it is due to fire */
+} timeout_params;
+
+/*
+ * List of possible timeout reasons in the order of enum TimeoutId.
+ */
+static timeout_params all_timeouts[MAX_TIMEOUTS];
+static bool all_timeouts_initialized = false;
+
+/*
+ * List of active timeouts ordered by their fin_time and priority.
+ * This list is subject to change by the interrupt handler, so it's volatile.
+ */
+static volatile int num_active_timeouts = 0;
+static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
+
+
+/*****************************************************************************
+ * Internal helper functions
+ *
+ * For all of these, it is caller's responsibility to protect them from
+ * interruption by the signal handler.
+ *****************************************************************************/
+
+/*
+ * Find the index of a given timeout reason in the active array.
+ * If it's not there, return -1.
+ */
+static int
+find_active_timeout(TimeoutId id)
+{
+	int			i;
+
+	for (i = 0; i < num_active_timeouts; i++)
+	{
+		if (active_timeouts[i]->index == id)
+			return i;
+	}
+
+	return -1;
+}
+
+/*
+ * Insert specified timeout reason into the list of active timeouts
+ * at the given index.
+ */
+static void
+insert_timeout(TimeoutId id, int index)
+{
+	int			i;
+
+	if (index < 0 || index > num_active_timeouts)
+		elog(FATAL, "timeout index %d out of range 0..%d", index,
+			 num_active_timeouts);
+
+	for (i = num_active_timeouts - 1; i >= index; i--)
+		active_timeouts[i + 1] = active_timeouts[i];
+
+	active_timeouts[index] = &all_timeouts[id];
+
+	/* NB: this must be the last step, see comments in enable_timeout */
+	num_active_timeouts++;
+}
+
+/*
+ * Remove the index'th element from the timeout list.
+ */
+static void
+remove_timeout_index(int index)
+{
+	int			i;
+
+	if (index < 0 || index >= num_active_timeouts)
+		elog(FATAL, "timeout index %d out of range 0..%d", index,
+			 num_active_timeouts - 1);
+
+	for (i = index + 1; i < num_active_timeouts; i++)
+		active_timeouts[i - 1] = active_timeouts[i];
+
+	num_active_timeouts--;
+}
+
+/*
+ * Schedule alarm for the next active timeout, if any
+ *
+ * We assume the caller has obtained the current time, or a close-enough
+ * approximation.
+ */
+static void
+schedule_alarm(TimestampTz now)
+{
+	if (num_active_timeouts > 0)
+	{
+		struct itimerval timeval;
+		long		secs;
+		int			usecs;
+
+		MemSet(&timeval, 0, sizeof(struct itimerval));
+
+		/* Get the time remaining till the nearest pending timeout */
+		TimestampDifference(now, active_timeouts[0]->fin_time,
+							&secs, &usecs);
+
+		/*
+		 * It's possible that the difference is less than a microsecond;
+		 * ensure we don't cancel, rather than set, the interrupt.
+		 */
+		if (secs == 0 && usecs == 0)
+			usecs = 1;
+
+		timeval.it_value.tv_sec = secs;
+		timeval.it_value.tv_usec = usecs;
+
+		/* Set the alarm timer */
+		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+			elog(FATAL, "could not enable SIGALRM timer: %m");
+	}
+}
+
+
+/*****************************************************************************
+ * Signal handler
+ *****************************************************************************/
+
+/*
+ * Signal handler for SIGALRM
+ *
+ * Process any active timeout reasons and then reschedule the interrupt
+ * as needed.
+ */
+static void
+handle_sig_alarm(SIGNAL_ARGS)
+{
+	int			save_errno = errno;
+
+	/*
+	 * 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.
+	 */
+	if (MyProc)
+		SetLatch(&MyProc->procLatch);
+
+	/*
+	 * Fire any pending timeouts.
+	 */
+	if (num_active_timeouts > 0)
+	{
+		TimestampTz now = GetCurrentTimestamp();
+
+		/* While the first pending timeout has been reached ... */
+		while (num_active_timeouts > 0 &&
+			   now >= active_timeouts[0]->fin_time)
+		{
+			timeout_params *this_timeout = active_timeouts[0];
+
+			/* Remove it from the active list */
+			remove_timeout_index(0);
+
+			/* Mark it as fired */
+			this_timeout->indicator = true;
+
+			/* And call its handler function */
+			(*this_timeout->timeout_handler) ();
+
+			/*
+			 * The handler might not take negligible time (CheckDeadLock for
+			 * instance isn't too cheap), so let's update our idea of "now"
+			 * after each one.
+			 */
+			now = GetCurrentTimestamp();
+		}
+
+		/* Done firing timeouts, so reschedule next interrupt if any */
+		schedule_alarm(now);
+	}
+
+	errno = save_errno;
+}
+
+
+/*****************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/*
+ * Initialize timeout module.
+ *
+ * This must be called in every process that wants to use timeouts.
+ *
+ * If the process was forked from another one that was also using this
+ * module, be sure to call this before re-enabling signals; else handlers
+ * meant to run in the parent process might get invoked in this one.
+ */
+void
+InitializeTimeouts(void)
+{
+	int			i;
+
+	/* Initialize, or re-initialize, all local state */
+	num_active_timeouts = 0;
+
+	for (i = 0; i < MAX_TIMEOUTS; i++)
+	{
+		all_timeouts[i].index = i;
+		all_timeouts[i].indicator = false;
+		all_timeouts[i].timeout_handler = NULL;
+		all_timeouts[i].start_time = 0;
+		all_timeouts[i].fin_time = 0;
+	}
+
+	all_timeouts_initialized = true;
+
+	/* Now establish the signal handler */
+	pqsignal(SIGALRM, handle_sig_alarm);
+}
+
+/*
+ * Register a timeout reason
+ *
+ * For predefined timeouts, this just registers the callback function.
+ *
+ * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
+ * return a timeout ID.
+ */
+TimeoutId
+RegisterTimeout(TimeoutId id, timeout_handler handler)
+{
+	Assert(all_timeouts_initialized);
+
+	if (id >= USER_TIMEOUT)
+	{
+		/* Allocate a user-defined timeout reason */
+		for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
+			if (all_timeouts[id].timeout_handler == NULL)
+				break;
+		if (id >= MAX_TIMEOUTS)
+			ereport(FATAL,
+					(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
+					 errmsg("cannot add more timeout reasons")));
+	}
+
+	Assert(all_timeouts[id].timeout_handler == NULL);
+
+	all_timeouts[id].timeout_handler = handler;
+
+	return id;
+}
+
+/*
+ * Enable the specified timeout reason
+ */
+static void
+enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
+{
+	struct itimerval timeval;
+	int			i;
+
+	/* Assert request is sane */
+	Assert(all_timeouts_initialized);
+	Assert(all_timeouts[id].timeout_handler != NULL);
+
+	/*
+	 * Disable the timer if it is active; this avoids getting interrupted by
+	 * the signal handler and thereby possibly getting confused.  We will
+	 * re-enable the interrupt below.
+	 *
+	 * If num_active_timeouts is zero, we don't have to call setitimer.  There
+	 * should not be any pending interrupt, and even if there is, the worst
+	 * possible case is that the signal handler fires during schedule_alarm.
+	 * (If it fires at any point before insert_timeout has incremented
+	 * num_active_timeouts, it will do nothing.)  In that case we could end up
+	 * scheduling a useless interrupt ... but when the interrupt does happen,
+	 * the signal handler will do nothing, so it's all good.
+	 */
+	if (num_active_timeouts > 0)
+	{
+		MemSet(&timeval, 0, sizeof(struct itimerval));
+		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+			elog(FATAL, "could not disable SIGALRM timer: %m");
+	}
+
+	/*
+	 * If this timeout was already active, momentarily disable it.	We
+	 * interpret the call as a directive to reschedule the timeout.
+	 */
+	i = find_active_timeout(id);
+	if (i >= 0)
+		remove_timeout_index(i);
+
+	/*
+	 * Find out the index where to insert the new timeout.	We sort by
+	 * fin_time, and for equal fin_time by priority.
+	 */
+	for (i = 0; i < num_active_timeouts; i++)
+	{
+		timeout_params *old_timeout = active_timeouts[i];
+
+		if (fin_time < old_timeout->fin_time)
+			break;
+		if (fin_time == old_timeout->fin_time && id < old_timeout->index)
+			break;
+	}
+
+	/*
+	 * Activate the timeout.
+	 */
+	all_timeouts[id].indicator = false;
+	all_timeouts[id].start_time = now;
+	all_timeouts[id].fin_time = fin_time;
+	insert_timeout(id, i);
+
+	/*
+	 * Set the timer.
+	 */
+	schedule_alarm(now);
+}
+
+/*
+ * Enable the specified timeout to fire after the specified delay.
+ *
+ * Delay is given in milliseconds.
+ */
+void
+enable_timeout_after(TimeoutId id, int delay_ms)
+{
+	TimestampTz now;
+	TimestampTz fin_time;
+
+	now = GetCurrentTimestamp();
+	fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
+
+	enable_timeout(id, now, fin_time);
+}
+
+/*
+ * Enable the specified timeout to fire at the specified time.
+ *
+ * This is provided to support cases where there's a reason to calculate
+ * the timeout by reference to some point other than "now".  If there isn't,
+ * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
+ */
+void
+enable_timeout_at(TimeoutId id, TimestampTz fin_time)
+{
+	enable_timeout(id, GetCurrentTimestamp(), fin_time);
+}
+
+/*
+ * Cancel the specified timeout.
+ *
+ * The timeout's I've-been-fired indicator is reset,
+ * unless keep_indicator is true.
+ *
+ * When a timeout is canceled, any other active timeout remains in force.
+ * It's not an error to disable a timeout that is not enabled.
+ */
+void
+disable_timeout(TimeoutId id, bool keep_indicator)
+{
+	struct itimerval timeval;
+	int			i;
+
+	/* Assert request is sane */
+	Assert(all_timeouts_initialized);
+	Assert(all_timeouts[id].timeout_handler != NULL);
+
+	/*
+	 * Disable the timer if it is active; this avoids getting interrupted by
+	 * the signal handler and thereby possibly getting confused.  We will
+	 * re-enable the interrupt if necessary below.
+	 *
+	 * If num_active_timeouts is zero, we don't have to call setitimer.  There
+	 * should not be any pending interrupt, and even if there is, the signal
+	 * handler will not do anything.  In this situation the only thing we
+	 * really have to do is reset the timeout's indicator.
+	 */
+	if (num_active_timeouts > 0)
+	{
+		MemSet(&timeval, 0, sizeof(struct itimerval));
+		if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+			elog(FATAL, "could not disable SIGALRM timer: %m");
+	}
+
+	/* Find the timeout and remove it from the active list. */
+	i = find_active_timeout(id);
+	if (i >= 0)
+		remove_timeout_index(i);
+
+	/* Mark it inactive, whether it was active or not. */
+	if (!keep_indicator)
+		all_timeouts[id].indicator = false;
+
+	/* Now re-enable the timer, if necessary. */
+	if (num_active_timeouts > 0)
+		schedule_alarm(GetCurrentTimestamp());
+}
+
+/*
+ * Disable SIGALRM and remove all timeouts from the active list,
+ * and optionally reset their timeout indicators.
+ */
+void
+disable_all_timeouts(bool keep_indicators)
+{
+	struct itimerval timeval;
+	int			i;
+
+	MemSet(&timeval, 0, sizeof(struct itimerval));
+	if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
+		elog(FATAL, "could not disable SIGALRM timer: %m");
+
+	num_active_timeouts = 0;
+
+	if (!keep_indicators)
+	{
+		for (i = 0; i < MAX_TIMEOUTS; i++)
+			all_timeouts[i].indicator = false;
+	}
+}
+
+/*
+ * Return the timeout's I've-been-fired indicator
+ */
+bool
+get_timeout_indicator(TimeoutId id)
+{
+	return all_timeouts[id].indicator;
+}
+
+/*
+ * Return the time when the timeout was most recently activated
+ *
+ * Note: will return 0 if timeout has never been activated in this process.
+ * However, we do *not* reset the start_time when a timeout occurs, so as
+ * not to create a race condition if SIGALRM fires just as some code is
+ * about to fetch the value.
+ */
+TimestampTz
+get_timeout_start_time(TimeoutId id)
+{
+	return all_timeouts[id].start_time;
+}
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 31f7099a635b12886444fa30c18ac9ebc62e73b1..e10aafe99e43d19d2346e5a43b240ffa339808dc 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -15,7 +15,6 @@
 #define _PROC_H_
 
 #include "access/xlogdefs.h"
-#include "datatype/timestamp.h"
 #include "storage/latch.h"
 #include "storage/lock.h"
 #include "storage/pg_sema.h"
@@ -222,8 +221,6 @@ extern int	DeadlockTimeout;
 extern int	StatementTimeout;
 extern bool log_lock_waits;
 
-extern volatile bool cancel_from_timeout;
-
 
 /*
  * Function Prototypes
@@ -246,19 +243,11 @@ extern void ProcQueueInit(PROC_QUEUE *queue);
 extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
 extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
 extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
+extern void CheckDeadLock(void);
 extern bool IsWaitingForLock(void);
 extern void LockErrorCleanup(void);
 
 extern void ProcWaitForSignal(void);
 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 */
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index ed3b66b35df07553a77a17619e54d52b4a981562..7024fc4f3c2d2d105ab6d5898b5e362a59937148 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -33,8 +33,9 @@ extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
 extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
 
 extern void ResolveRecoveryConflictWithBufferPin(void);
-extern void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
 extern void CheckRecoveryConflictDeadlock(void);
+extern void StandbyDeadLockHandler(void);
+extern void StandbyTimeoutHandler(void);
 
 /*
  * Standby Rmgr (RM_STANDBY_ID)
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
new file mode 100644
index 0000000000000000000000000000000000000000..76a7e1a63e468c8aebf96750ce8ebfcffb72278f
--- /dev/null
+++ b/src/include/utils/timeout.h
@@ -0,0 +1,54 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */