aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Haas <rhaas@postgresql.org>2021-09-30 12:04:50 -0400
committerRobert Haas <rhaas@postgresql.org>2021-10-25 11:33:44 -0400
commit732e6677a667c03b1551a855e3216644b0f125ec (patch)
treebeae396a87ef08adad6d3e76b1e238d07e0aa9b0 /src
parent902a2c280012557b85c7e0fce3f6f0e355cb2d69 (diff)
downloadpostgresql-732e6677a667c03b1551a855e3216644b0f125ec.tar.gz
postgresql-732e6677a667c03b1551a855e3216644b0f125ec.zip
Add enable_timeout_every() to fire the same timeout repeatedly.
enable_timeout_at() and enable_timeout_after() can still be used when you want to fire a timeout just once. Patch by me, per a suggestion from Tom Lane. Discussion: http://postgr.es/m/2992585.1632938816@sss.pgh.pa.us Discussion: http://postgr.es/m/CA+TgmoYqSF5sCNrgTom9r3Nh=at4WmYFD=gsV-omStZ60S0ZUQ@mail.gmail.com
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/misc/timeout.c65
-rw-r--r--src/include/utils/timeout.h7
2 files changed, 65 insertions, 7 deletions
diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c
index 95a273d9cfb..af74e99ed1a 100644
--- a/src/backend/utils/misc/timeout.c
+++ b/src/backend/utils/misc/timeout.c
@@ -36,6 +36,7 @@ typedef struct timeout_params
TimestampTz start_time; /* time that timeout was last activated */
TimestampTz fin_time; /* time it is, or was last, due to fire */
+ int interval_in_ms; /* time between firings, or 0 if just once */
} timeout_params;
/*
@@ -153,7 +154,8 @@ remove_timeout_index(int index)
* Enable the specified timeout reason
*/
static void
-enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
+enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time,
+ int interval_in_ms)
{
int i;
@@ -188,6 +190,7 @@ enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
all_timeouts[id].indicator = false;
all_timeouts[id].start_time = now;
all_timeouts[id].fin_time = fin_time;
+ all_timeouts[id].interval_in_ms = interval_in_ms;
insert_timeout(id, i);
}
@@ -399,6 +402,29 @@ handle_sig_alarm(SIGNAL_ARGS)
/* And call its handler function */
this_timeout->timeout_handler();
+ /* If it should fire repeatedly, re-enable it. */
+ if (this_timeout->interval_in_ms > 0)
+ {
+ TimestampTz new_fin_time;
+
+ /*
+ * To guard against drift, schedule the next instance of
+ * the timeout based on the intended firing time rather
+ * than the actual firing time. But if the timeout was so
+ * late that we missed an entire cycle, fall back to
+ * scheduling based on the actual firing time.
+ */
+ new_fin_time =
+ TimestampTzPlusMilliseconds(this_timeout->fin_time,
+ this_timeout->interval_in_ms);
+ if (new_fin_time < now)
+ new_fin_time =
+ TimestampTzPlusMilliseconds(now,
+ this_timeout->interval_in_ms);
+ enable_timeout(this_timeout->index, now, new_fin_time,
+ this_timeout->interval_in_ms);
+ }
+
/*
* The handler might not take negligible time (CheckDeadLock
* for instance isn't too cheap), so let's update our idea of
@@ -449,6 +475,7 @@ InitializeTimeouts(void)
all_timeouts[i].timeout_handler = NULL;
all_timeouts[i].start_time = 0;
all_timeouts[i].fin_time = 0;
+ all_timeouts[i].interval_in_ms = 0;
}
all_timeouts_initialized = true;
@@ -532,7 +559,29 @@ enable_timeout_after(TimeoutId id, int delay_ms)
/* Queue the timeout at the appropriate time. */
now = GetCurrentTimestamp();
fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
- enable_timeout(id, now, fin_time);
+ enable_timeout(id, now, fin_time, 0);
+
+ /* Set the timer interrupt. */
+ schedule_alarm(now);
+}
+
+/*
+ * Enable the specified timeout to fire periodically, with the specified
+ * delay as the time between firings.
+ *
+ * Delay is given in milliseconds.
+ */
+void
+enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
+{
+ TimestampTz now;
+
+ /* Disable timeout interrupts for safety. */
+ disable_alarm();
+
+ /* Queue the timeout at the appropriate time. */
+ now = GetCurrentTimestamp();
+ enable_timeout(id, now, fin_time, delay_ms);
/* Set the timer interrupt. */
schedule_alarm(now);
@@ -555,7 +604,7 @@ enable_timeout_at(TimeoutId id, TimestampTz fin_time)
/* Queue the timeout at the appropriate time. */
now = GetCurrentTimestamp();
- enable_timeout(id, now, fin_time);
+ enable_timeout(id, now, fin_time, 0);
/* Set the timer interrupt. */
schedule_alarm(now);
@@ -590,11 +639,17 @@ enable_timeouts(const EnableTimeoutParams *timeouts, int count)
case TMPARAM_AFTER:
fin_time = TimestampTzPlusMilliseconds(now,
timeouts[i].delay_ms);
- enable_timeout(id, now, fin_time);
+ enable_timeout(id, now, fin_time, 0);
break;
case TMPARAM_AT:
- enable_timeout(id, now, timeouts[i].fin_time);
+ enable_timeout(id, now, timeouts[i].fin_time, 0);
+ break;
+
+ case TMPARAM_EVERY:
+ fin_time = TimestampTzPlusMilliseconds(now,
+ timeouts[i].delay_ms);
+ enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
break;
default:
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
index 93e6a691b3f..1b13ac96e0e 100644
--- a/src/include/utils/timeout.h
+++ b/src/include/utils/timeout.h
@@ -48,14 +48,15 @@ typedef void (*timeout_handler_proc) (void);
typedef enum TimeoutType
{
TMPARAM_AFTER,
- TMPARAM_AT
+ TMPARAM_AT,
+ TMPARAM_EVERY
} TimeoutType;
typedef struct
{
TimeoutId id; /* timeout to set */
TimeoutType type; /* TMPARAM_AFTER or TMPARAM_AT */
- int delay_ms; /* only used for TMPARAM_AFTER */
+ int delay_ms; /* only used for TMPARAM_AFTER/EVERY */
TimestampTz fin_time; /* only used for TMPARAM_AT */
} EnableTimeoutParams;
@@ -75,6 +76,8 @@ extern void reschedule_timeouts(void);
/* timeout operation */
extern void enable_timeout_after(TimeoutId id, int delay_ms);
+extern void enable_timeout_every(TimeoutId id, TimestampTz fin_time,
+ int delay_ms);
extern void enable_timeout_at(TimeoutId id, TimestampTz fin_time);
extern void enable_timeouts(const EnableTimeoutParams *timeouts, int count);
extern void disable_timeout(TimeoutId id, bool keep_indicator);