if (QueryCancelPending)
{
+ bool lock_timeout_occurred;
+ bool stmt_timeout_occurred;
+
/*
* Don't allow query cancel interrupts while reading input from the
* client, because we might lose sync in the FE/BE protocol. (Die
/*
* If LOCK_TIMEOUT and STATEMENT_TIMEOUT indicators are both set, we
- * prefer to report the former; but be sure to clear both.
+ * need to clear both, so always fetch both.
*/
- if (get_timeout_indicator(LOCK_TIMEOUT, true))
+ lock_timeout_occurred = get_timeout_indicator(LOCK_TIMEOUT, true);
+ stmt_timeout_occurred = get_timeout_indicator(STATEMENT_TIMEOUT, true);
+
+ /*
+ * If both were set, we want to report whichever timeout completed
+ * earlier; this ensures consistent behavior if the machine is slow
+ * enough that the second timeout triggers before we get here. A tie
+ * is arbitrarily broken in favor of reporting a lock timeout.
+ */
+ if (lock_timeout_occurred && stmt_timeout_occurred &&
+ get_timeout_finish_time(STATEMENT_TIMEOUT) < get_timeout_finish_time(LOCK_TIMEOUT))
+ lock_timeout_occurred = false; /* report stmt timeout */
+
+ if (lock_timeout_occurred)
{
ImmediateInterruptOK = false; /* not idle anymore */
- (void) get_timeout_indicator(STATEMENT_TIMEOUT, true);
LockErrorCleanup();
DisableNotifyInterrupt();
DisableCatchupInterrupt();
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("canceling statement due to lock timeout")));
}
- if (get_timeout_indicator(STATEMENT_TIMEOUT, true))
+ if (stmt_timeout_occurred)
{
ImmediateInterruptOK = false; /* not idle anymore */
LockErrorCleanup();
timeout_handler_proc timeout_handler;
TimestampTz start_time; /* time that timeout was last activated */
- TimestampTz fin_time; /* if active, time it is due to fire */
+ TimestampTz fin_time; /* time it is, or was last, due to fire */
} timeout_params;
/*
{
return all_timeouts[id].start_time;
}
+
+/*
+ * Return the time when the timeout is, or most recently was, due to fire
+ *
+ * Note: will return 0 if timeout has never been activated in this process.
+ * However, we do *not* reset the fin_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_finish_time(TimeoutId id)
+{
+ return all_timeouts[id].fin_time;
+}