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
This commit is contained in:
Robert Haas 2021-09-30 12:04:50 -04:00
parent 902a2c2800
commit 732e6677a6
2 changed files with 65 additions and 7 deletions

View File

@ -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:

View File

@ -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);