Skip to content

Commit 732e667

Browse files
committed
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
1 parent 902a2c2 commit 732e667

File tree

2 files changed

+65
-7
lines changed

2 files changed

+65
-7
lines changed

src/backend/utils/misc/timeout.c

+60-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ typedef struct timeout_params
3636

3737
TimestampTz start_time; /* time that timeout was last activated */
3838
TimestampTz fin_time; /* time it is, or was last, due to fire */
39+
int interval_in_ms; /* time between firings, or 0 if just once */
3940
} timeout_params;
4041

4142
/*
@@ -153,7 +154,8 @@ remove_timeout_index(int index)
153154
* Enable the specified timeout reason
154155
*/
155156
static void
156-
enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
157+
enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time,
158+
int interval_in_ms)
157159
{
158160
int i;
159161

@@ -188,6 +190,7 @@ enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
188190
all_timeouts[id].indicator = false;
189191
all_timeouts[id].start_time = now;
190192
all_timeouts[id].fin_time = fin_time;
193+
all_timeouts[id].interval_in_ms = interval_in_ms;
191194

192195
insert_timeout(id, i);
193196
}
@@ -399,6 +402,29 @@ handle_sig_alarm(SIGNAL_ARGS)
399402
/* And call its handler function */
400403
this_timeout->timeout_handler();
401404

405+
/* If it should fire repeatedly, re-enable it. */
406+
if (this_timeout->interval_in_ms > 0)
407+
{
408+
TimestampTz new_fin_time;
409+
410+
/*
411+
* To guard against drift, schedule the next instance of
412+
* the timeout based on the intended firing time rather
413+
* than the actual firing time. But if the timeout was so
414+
* late that we missed an entire cycle, fall back to
415+
* scheduling based on the actual firing time.
416+
*/
417+
new_fin_time =
418+
TimestampTzPlusMilliseconds(this_timeout->fin_time,
419+
this_timeout->interval_in_ms);
420+
if (new_fin_time < now)
421+
new_fin_time =
422+
TimestampTzPlusMilliseconds(now,
423+
this_timeout->interval_in_ms);
424+
enable_timeout(this_timeout->index, now, new_fin_time,
425+
this_timeout->interval_in_ms);
426+
}
427+
402428
/*
403429
* The handler might not take negligible time (CheckDeadLock
404430
* for instance isn't too cheap), so let's update our idea of
@@ -449,6 +475,7 @@ InitializeTimeouts(void)
449475
all_timeouts[i].timeout_handler = NULL;
450476
all_timeouts[i].start_time = 0;
451477
all_timeouts[i].fin_time = 0;
478+
all_timeouts[i].interval_in_ms = 0;
452479
}
453480

454481
all_timeouts_initialized = true;
@@ -532,7 +559,29 @@ enable_timeout_after(TimeoutId id, int delay_ms)
532559
/* Queue the timeout at the appropriate time. */
533560
now = GetCurrentTimestamp();
534561
fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
535-
enable_timeout(id, now, fin_time);
562+
enable_timeout(id, now, fin_time, 0);
563+
564+
/* Set the timer interrupt. */
565+
schedule_alarm(now);
566+
}
567+
568+
/*
569+
* Enable the specified timeout to fire periodically, with the specified
570+
* delay as the time between firings.
571+
*
572+
* Delay is given in milliseconds.
573+
*/
574+
void
575+
enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
576+
{
577+
TimestampTz now;
578+
579+
/* Disable timeout interrupts for safety. */
580+
disable_alarm();
581+
582+
/* Queue the timeout at the appropriate time. */
583+
now = GetCurrentTimestamp();
584+
enable_timeout(id, now, fin_time, delay_ms);
536585

537586
/* Set the timer interrupt. */
538587
schedule_alarm(now);
@@ -555,7 +604,7 @@ enable_timeout_at(TimeoutId id, TimestampTz fin_time)
555604

556605
/* Queue the timeout at the appropriate time. */
557606
now = GetCurrentTimestamp();
558-
enable_timeout(id, now, fin_time);
607+
enable_timeout(id, now, fin_time, 0);
559608

560609
/* Set the timer interrupt. */
561610
schedule_alarm(now);
@@ -590,11 +639,17 @@ enable_timeouts(const EnableTimeoutParams *timeouts, int count)
590639
case TMPARAM_AFTER:
591640
fin_time = TimestampTzPlusMilliseconds(now,
592641
timeouts[i].delay_ms);
593-
enable_timeout(id, now, fin_time);
642+
enable_timeout(id, now, fin_time, 0);
594643
break;
595644

596645
case TMPARAM_AT:
597-
enable_timeout(id, now, timeouts[i].fin_time);
646+
enable_timeout(id, now, timeouts[i].fin_time, 0);
647+
break;
648+
649+
case TMPARAM_EVERY:
650+
fin_time = TimestampTzPlusMilliseconds(now,
651+
timeouts[i].delay_ms);
652+
enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
598653
break;
599654

600655
default:

src/include/utils/timeout.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,15 @@ typedef void (*timeout_handler_proc) (void);
4848
typedef enum TimeoutType
4949
{
5050
TMPARAM_AFTER,
51-
TMPARAM_AT
51+
TMPARAM_AT,
52+
TMPARAM_EVERY
5253
} TimeoutType;
5354

5455
typedef struct
5556
{
5657
TimeoutId id; /* timeout to set */
5758
TimeoutType type; /* TMPARAM_AFTER or TMPARAM_AT */
58-
int delay_ms; /* only used for TMPARAM_AFTER */
59+
int delay_ms; /* only used for TMPARAM_AFTER/EVERY */
5960
TimestampTz fin_time; /* only used for TMPARAM_AT */
6061
} EnableTimeoutParams;
6162

@@ -75,6 +76,8 @@ extern void reschedule_timeouts(void);
7576

7677
/* timeout operation */
7778
extern void enable_timeout_after(TimeoutId id, int delay_ms);
79+
extern void enable_timeout_every(TimeoutId id, TimestampTz fin_time,
80+
int delay_ms);
7881
extern void enable_timeout_at(TimeoutId id, TimestampTz fin_time);
7982
extern void enable_timeouts(const EnableTimeoutParams *timeouts, int count);
8083
extern void disable_timeout(TimeoutId id, bool keep_indicator);

0 commit comments

Comments
 (0)