#include "commands/trigger.h"
 #include "executor/spi.h"
 #include "libpq/be-fsstubs.h"
+#include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
 #include "replication/walsender.h"
 #include "utils/memutils.h"
 #include "utils/relmapper.h"
 #include "utils/snapmgr.h"
+#include "utils/timeout.h"
 #include "utils/timestamp.h"
 #include "pg_trace.h"
 
     */
    LockErrorCleanup();
 
+   /*
+    * If any timeout events are still active, make sure the timeout interrupt
+    * is scheduled.  This covers possible loss of a timeout interrupt due to
+    * longjmp'ing out of the SIGINT handler (see notes in handle_sig_alarm).
+    * We delay this till after LockErrorCleanup so that we don't uselessly
+    * reschedule lock or deadlock check timeouts.
+    */
+   reschedule_timeouts();
+
+   /*
+    * Re-enable signals, in case we got here by longjmp'ing out of a signal
+    * handler.  We do this fairly early in the sequence so that the timeout
+    * infrastructure will be functional if needed while aborting.
+    */
+   PG_SETMASK(&UnBlockSig);
+
    /*
     * check the current transaction state
     */
    AbortBufferIO();
    UnlockBuffers();
 
+   /*
+    * Also clean up any open wait for lock, since the lock manager will choke
+    * if we try to wait for another lock before doing this.
+    */
    LockErrorCleanup();
 
+   /*
+    * If any timeout events are still active, make sure the timeout interrupt
+    * is scheduled.  This covers possible loss of a timeout interrupt due to
+    * longjmp'ing out of the SIGINT handler (see notes in handle_sig_alarm).
+    * We delay this till after LockErrorCleanup so that we don't uselessly
+    * reschedule lock or deadlock check timeouts.
+    */
+   reschedule_timeouts();
+
+   /*
+    * Re-enable signals, in case we got here by longjmp'ing out of a signal
+    * handler.  We do this fairly early in the sequence so that the timeout
+    * infrastructure will be functional if needed while aborting.
+    */
+   PG_SETMASK(&UnBlockSig);
+
    /*
     * check the current transaction state
     */
 
        HOLD_INTERRUPTS();
 
        /* Forget any pending QueryCancel or timeout request */
-       QueryCancelPending = false;
        disable_all_timeouts(false);
-       QueryCancelPending = false;     /* again in case timeout occurred */
+       QueryCancelPending = false;     /* second to avoid race condition */
 
        /* Report the error to the server log */
        EmitErrorReport();
 
    } while (myWaitStatus == STATUS_WAITING);
 
    /*
-    * Disable the timers, if they are still running
+    * Disable the timers, if they are still running.  As in LockErrorCleanup,
+    * we must preserve the LOCK_TIMEOUT indicator flag: if a lock timeout has
+    * already caused QueryCancelPending to become set, we want the cancel to
+    * be reported as a lock timeout, not a user cancel.
     */
    if (LockTimeout > 0)
    {
        timeouts[0].id = DEADLOCK_TIMEOUT;
        timeouts[0].keep_indicator = false;
        timeouts[1].id = LOCK_TIMEOUT;
-       timeouts[1].keep_indicator = false;
+       timeouts[1].keep_indicator = true;
        disable_timeouts(timeouts, 2);
    }
    else
 
     * always active, we have at least some chance of recovering from an error
     * during error recovery.  (If we get into an infinite loop thereby, it
     * will soon be stopped by overflow of elog.c's internal state stack.)
+    *
+    * Note that we use sigsetjmp(..., 1), so that this function's signal mask
+    * (to wit, UnBlockSig) will be restored when longjmp'ing to here.  This
+    * is essential in case we longjmp'd out of a signal handler on a platform
+    * where that leaves the signal blocked.  It's not redundant with the
+    * unblock in AbortTransaction() because the latter is only called if we
+    * were inside a transaction.
     */
 
    if (sigsetjmp(local_sigjmp_buf, 1) != 0)
 
        /*
         * Forget any pending QueryCancel request, since we're returning to
-        * the idle loop anyway, and cancel any active timeout requests.
+        * the idle loop anyway, and cancel any active timeout requests.  (In
+        * future we might want to allow some timeout requests to survive, but
+        * at minimum it'd be necessary to do reschedule_timeouts(), in case
+        * we got here because of a query cancel interrupting the SIGALRM
+        * interrupt handler.)  Note in particular that we must clear the
+        * statement and lock timeout indicators, to prevent any future plain
+        * query cancels from being misreported as timeouts in case we're
+        * forgetting a timeout cancel.
         */
-       QueryCancelPending = false;
        disable_all_timeouts(false);
-       QueryCancelPending = false;     /* again in case timeout occurred */
+       QueryCancelPending = false;     /* second to avoid race condition */
 
        /*
         * Turn off these interrupts too.  This is only needed here and not in
 
 
 #include <sys/time.h>
 
+#include "miscadmin.h"
 #include "storage/proc.h"
 #include "utils/timeout.h"
 #include "utils/timestamp.h"
 {
    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
        }
    }
 
+   /*
+    * 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;
 }
 
    return id;
 }
 
+/*
+ * 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.
  *
 
 /* timeout setup */
 extern void InitializeTimeouts(void);
 extern TimeoutId RegisterTimeout(TimeoutId id, timeout_handler_proc handler);
+extern void reschedule_timeouts(void);
 
 /* timeout operation */
 extern void enable_timeout_after(TimeoutId id, int delay_ms);