aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/timeout.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/misc/timeout.c')
-rw-r--r--src/backend/utils/misc/timeout.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index ac570730337..6f5e08bc302 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -71,11 +71,12 @@ static volatile sig_atomic_t alarm_enabled = false;
/*
* State recording if and when we next expect the interrupt to fire.
+ * (signal_due_at is valid only when signal_pending is true.)
* Note that the signal handler will unconditionally reset signal_pending to
* false, so that can change asynchronously even when alarm_enabled is false.
*/
static volatile sig_atomic_t signal_pending = false;
-static TimestampTz signal_due_at = 0; /* valid only when signal_pending */
+static volatile TimestampTz signal_due_at = 0;
/*****************************************************************************
@@ -218,9 +219,21 @@ schedule_alarm(TimestampTz now)
MemSet(&timeval, 0, sizeof(struct itimerval));
/*
+ * If we think there's a signal pending, but current time is more than
+ * 10ms past when the signal was due, then assume that the timeout
+ * request got lost somehow; clear signal_pending so that we'll reset
+ * the interrupt request below. (10ms corresponds to the worst-case
+ * timeout granularity on modern systems.) It won't hurt us if the
+ * interrupt does manage to fire between now and when we reach the
+ * setitimer() call.
+ */
+ if (signal_pending && now > signal_due_at + 10 * 1000)
+ signal_pending = false;
+
+ /*
* Get the time remaining till the nearest pending timeout. If it is
- * negative, assume that we somehow missed an interrupt, and force
- * signal_pending off. This gives us a chance to recover if the
+ * negative, assume that we somehow missed an interrupt, and clear
+ * signal_pending. This gives us another chance to recover if the
* kernel drops a timeout request for some reason.
*/
nearest_timeout = active_timeouts[0]->fin_time;