aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/tcop/postgres.c23
-rw-r--r--src/backend/utils/misc/timeout.c16
-rw-r--r--src/include/utils/timeout.h1
3 files changed, 35 insertions, 5 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 68811f1f217..b185c1b5eb6 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -2909,6 +2909,9 @@ ProcessInterrupts(void)
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
@@ -2929,17 +2932,29 @@ ProcessInterrupts(void)
/*
* 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)
{
- (void) get_timeout_indicator(STATEMENT_TIMEOUT, true);
LockErrorCleanup();
ereport(ERROR,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("canceling statement due to lock timeout")));
}
- if (get_timeout_indicator(STATEMENT_TIMEOUT, true))
+ if (stmt_timeout_occurred)
{
LockErrorCleanup();
ereport(ERROR,
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index 3b3f220e6e6..7171a7c59ce 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -34,7 +34,7 @@ typedef struct timeout_params
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;
/*
@@ -654,3 +654,17 @@ get_timeout_start_time(TimeoutId id)
{
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;
+}
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index f64921e2d67..260c0139609 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -82,5 +82,6 @@ extern void disable_all_timeouts(bool keep_indicators);
/* accessors */
extern bool get_timeout_indicator(TimeoutId id, bool reset_indicator);
extern TimestampTz get_timeout_start_time(TimeoutId id);
+extern TimestampTz get_timeout_finish_time(TimeoutId id);
#endif /* TIMEOUT_H */