From 732e6677a667c03b1551a855e3216644b0f125ec Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Thu, 30 Sep 2021 12:04:50 -0400 Subject: [PATCH] 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 --- src/backend/utils/misc/timeout.c | 65 +++++++++++++++++++++++++++++--- src/include/utils/timeout.h | 7 +++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/backend/utils/misc/timeout.c b/src/backend/utils/misc/timeout.c index 95a273d9cf..af74e99ed1 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 93e6a691b3..1b13ac96e0 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);