PostgreSQL Source Code git master
timeout.c File Reference
#include "postgres.h"
#include <sys/time.h>
#include "miscadmin.h"
#include "storage/latch.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
Include dependency graph for timeout.c:

Go to the source code of this file.

Data Structures

struct  timeout_params
 

Macros

#define disable_alarm()   (alarm_enabled = false)
 
#define enable_alarm()   (alarm_enabled = true)
 

Typedefs

typedef struct timeout_params timeout_params
 

Functions

static int find_active_timeout (TimeoutId id)
 
static void insert_timeout (TimeoutId id, int index)
 
static void remove_timeout_index (int index)
 
static void enable_timeout (TimeoutId id, TimestampTz now, TimestampTz fin_time, int interval_in_ms)
 
static void schedule_alarm (TimestampTz now)
 
static void handle_sig_alarm (SIGNAL_ARGS)
 
void InitializeTimeouts (void)
 
TimeoutId RegisterTimeout (TimeoutId id, timeout_handler_proc handler)
 
void reschedule_timeouts (void)
 
void enable_timeout_after (TimeoutId id, int delay_ms)
 
void enable_timeout_every (TimeoutId id, TimestampTz fin_time, int delay_ms)
 
void enable_timeout_at (TimeoutId id, TimestampTz fin_time)
 
void enable_timeouts (const EnableTimeoutParams *timeouts, int count)
 
void disable_timeout (TimeoutId id, bool keep_indicator)
 
void disable_timeouts (const DisableTimeoutParams *timeouts, int count)
 
void disable_all_timeouts (bool keep_indicators)
 
bool get_timeout_active (TimeoutId id)
 
bool get_timeout_indicator (TimeoutId id, bool reset_indicator)
 
TimestampTz get_timeout_start_time (TimeoutId id)
 
TimestampTz get_timeout_finish_time (TimeoutId id)
 

Variables

static timeout_params all_timeouts [MAX_TIMEOUTS]
 
static bool all_timeouts_initialized = false
 
static volatile int num_active_timeouts = 0
 
static timeout_params *volatile active_timeouts [MAX_TIMEOUTS]
 
static volatile sig_atomic_t alarm_enabled = false
 
static volatile sig_atomic_t signal_pending = false
 
static volatile TimestampTz signal_due_at = 0
 

Macro Definition Documentation

◆ disable_alarm

#define disable_alarm ( )    (alarm_enabled = false)

Definition at line 69 of file timeout.c.

◆ enable_alarm

#define enable_alarm ( )    (alarm_enabled = true)

Definition at line 70 of file timeout.c.

Typedef Documentation

◆ timeout_params

Function Documentation

◆ disable_all_timeouts()

void disable_all_timeouts ( bool  keep_indicators)

Definition at line 751 of file timeout.c.

752{
753 int i;
754
756
757 /*
758 * We used to disable the timer interrupt here, but in common usage
759 * patterns it's cheaper to leave it enabled; that may save us from having
760 * to enable it again shortly. See comments in schedule_alarm().
761 */
762
764
765 for (i = 0; i < MAX_TIMEOUTS; i++)
766 {
767 all_timeouts[i].active = false;
768 if (!keep_indicators)
769 all_timeouts[i].indicator = false;
770 }
771}
int i
Definition: isn.c:72
volatile bool active
Definition: timeout.c:31
volatile bool indicator
Definition: timeout.c:32
#define disable_alarm()
Definition: timeout.c:69
static timeout_params all_timeouts[MAX_TIMEOUTS]
Definition: timeout.c:45
static volatile int num_active_timeouts
Definition: timeout.c:52
@ MAX_TIMEOUTS
Definition: timeout.h:42

References timeout_params::active, all_timeouts, disable_alarm, i, timeout_params::indicator, MAX_TIMEOUTS, and num_active_timeouts.

Referenced by pgarch_archiveXlog(), PostgresMain(), ResolveRecoveryConflictWithBufferPin(), and ResolveRecoveryConflictWithLock().

◆ disable_timeout()

void disable_timeout ( TimeoutId  id,
bool  keep_indicator 
)

Definition at line 685 of file timeout.c.

686{
687 /* Assert request is sane */
689 Assert(all_timeouts[id].timeout_handler != NULL);
690
691 /* Disable timeout interrupts for safety. */
693
694 /* Find the timeout and remove it from the active list. */
695 if (all_timeouts[id].active)
697
698 /* Mark it inactive, whether it was active or not. */
699 if (!keep_indicator)
700 all_timeouts[id].indicator = false;
701
702 /* Reschedule the interrupt, if any timeouts remain active. */
703 if (num_active_timeouts > 0)
705}
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1644
#define Assert(condition)
Definition: c.h:812
static int find_active_timeout(TimeoutId id)
Definition: timeout.c:96
static bool all_timeouts_initialized
Definition: timeout.c:46
static void schedule_alarm(TimestampTz now)
Definition: timeout.c:210
static void remove_timeout_index(int index)
Definition: timeout.c:137

References all_timeouts, all_timeouts_initialized, Assert, disable_alarm, find_active_timeout(), GetCurrentTimestamp(), timeout_params::indicator, num_active_timeouts, remove_timeout_index(), and schedule_alarm().

Referenced by AbortTransaction(), assign_transaction_timeout(), BackendInitialize(), CommitTransaction(), disable_startup_progress_timeout(), disable_statement_timeout(), enable_statement_timeout(), PerformAuthentication(), PostgresMain(), PrepareTransaction(), and ProcSleep().

◆ disable_timeouts()

void disable_timeouts ( const DisableTimeoutParams timeouts,
int  count 
)

Definition at line 718 of file timeout.c.

719{
720 int i;
721
723
724 /* Disable timeout interrupts for safety. */
726
727 /* Cancel the timeout(s). */
728 for (i = 0; i < count; i++)
729 {
730 TimeoutId id = timeouts[i].id;
731
732 Assert(all_timeouts[id].timeout_handler != NULL);
733
734 if (all_timeouts[id].active)
736
737 if (!timeouts[i].keep_indicator)
738 all_timeouts[id].indicator = false;
739 }
740
741 /* Reschedule the interrupt, if any timeouts remain active. */
742 if (num_active_timeouts > 0)
744}
TimeoutId id
Definition: timeout.h:71
TimeoutId
Definition: timeout.h:24

References all_timeouts, all_timeouts_initialized, Assert, disable_alarm, find_active_timeout(), GetCurrentTimestamp(), i, DisableTimeoutParams::id, timeout_params::indicator, num_active_timeouts, remove_timeout_index(), and schedule_alarm().

Referenced by LockErrorCleanup(), and ProcSleep().

◆ enable_timeout()

static void enable_timeout ( TimeoutId  id,
TimestampTz  now,
TimestampTz  fin_time,
int  interval_in_ms 
)
static

Definition at line 158 of file timeout.c.

160{
161 int i;
162
163 /* Assert request is sane */
165 Assert(all_timeouts[id].timeout_handler != NULL);
166
167 /*
168 * If this timeout was already active, momentarily disable it. We
169 * interpret the call as a directive to reschedule the timeout.
170 */
171 if (all_timeouts[id].active)
173
174 /*
175 * Find out the index where to insert the new timeout. We sort by
176 * fin_time, and for equal fin_time by priority.
177 */
178 for (i = 0; i < num_active_timeouts; i++)
179 {
180 timeout_params *old_timeout = active_timeouts[i];
181
182 if (fin_time < old_timeout->fin_time)
183 break;
184 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
185 break;
186 }
187
188 /*
189 * Mark the timeout active, and insert it into the active list.
190 */
191 all_timeouts[id].indicator = false;
193 all_timeouts[id].fin_time = fin_time;
194 all_timeouts[id].interval_in_ms = interval_in_ms;
195
196 insert_timeout(id, i);
197}
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1608
Definition: type.h:96
int interval_in_ms
Definition: timeout.c:39
TimestampTz start_time
Definition: timeout.c:37
TimestampTz fin_time
Definition: timeout.c:38
static timeout_params *volatile active_timeouts[MAX_TIMEOUTS]
Definition: timeout.c:53
static void insert_timeout(TimeoutId id, int index)
Definition: timeout.c:114

References active_timeouts, all_timeouts, all_timeouts_initialized, Assert, timeout_params::fin_time, find_active_timeout(), i, timeout_params::indicator, insert_timeout(), timeout_params::interval_in_ms, now(), num_active_timeouts, remove_timeout_index(), and timeout_params::start_time.

Referenced by enable_timeout_after(), enable_timeout_at(), enable_timeout_every(), enable_timeouts(), and handle_sig_alarm().

◆ enable_timeout_after()

void enable_timeout_after ( TimeoutId  id,
int  delay_ms 
)

Definition at line 560 of file timeout.c.

561{
563 TimestampTz fin_time;
564
565 /* Disable timeout interrupts for safety. */
567
568 /* Queue the timeout at the appropriate time. */
570 fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
571 enable_timeout(id, now, fin_time, 0);
572
573 /* Set the timer interrupt. */
575}
int64 TimestampTz
Definition: timestamp.h:39
static void enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time, int interval_in_ms)
Definition: timeout.c:158
#define TimestampTzPlusMilliseconds(tz, ms)
Definition: timestamp.h:85

References disable_alarm, enable_timeout(), GetCurrentTimestamp(), now(), schedule_alarm(), and TimestampTzPlusMilliseconds.

Referenced by assign_transaction_timeout(), BackendInitialize(), enable_statement_timeout(), PerformAuthentication(), PostgresMain(), ProcessInterrupts(), ProcSleep(), start_xact_command(), and StartTransaction().

◆ enable_timeout_at()

void enable_timeout_at ( TimeoutId  id,
TimestampTz  fin_time 
)

Definition at line 607 of file timeout.c.

608{
610
611 /* Disable timeout interrupts for safety. */
613
614 /* Queue the timeout at the appropriate time. */
616 enable_timeout(id, now, fin_time, 0);
617
618 /* Set the timer interrupt. */
620}

References disable_alarm, enable_timeout(), GetCurrentTimestamp(), now(), and schedule_alarm().

◆ enable_timeout_every()

void enable_timeout_every ( TimeoutId  id,
TimestampTz  fin_time,
int  delay_ms 
)

Definition at line 584 of file timeout.c.

585{
587
588 /* Disable timeout interrupts for safety. */
590
591 /* Queue the timeout at the appropriate time. */
593 enable_timeout(id, now, fin_time, delay_ms);
594
595 /* Set the timer interrupt. */
597}

References disable_alarm, enable_timeout(), GetCurrentTimestamp(), now(), and schedule_alarm().

Referenced by enable_startup_progress_timeout().

◆ enable_timeouts()

void enable_timeouts ( const EnableTimeoutParams timeouts,
int  count 
)

Definition at line 630 of file timeout.c.

631{
633 int i;
634
635 /* Disable timeout interrupts for safety. */
637
638 /* Queue the timeout(s) at the appropriate times. */
640
641 for (i = 0; i < count; i++)
642 {
643 TimeoutId id = timeouts[i].id;
644 TimestampTz fin_time;
645
646 switch (timeouts[i].type)
647 {
648 case TMPARAM_AFTER:
650 timeouts[i].delay_ms);
651 enable_timeout(id, now, fin_time, 0);
652 break;
653
654 case TMPARAM_AT:
655 enable_timeout(id, now, timeouts[i].fin_time, 0);
656 break;
657
658 case TMPARAM_EVERY:
660 timeouts[i].delay_ms);
661 enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
662 break;
663
664 default:
665 elog(ERROR, "unrecognized timeout type %d",
666 (int) timeouts[i].type);
667 break;
668 }
669 }
670
671 /* Set the timer interrupt. */
673}
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
TimeoutId id
Definition: timeout.h:60
@ TMPARAM_AT
Definition: timeout.h:54
@ TMPARAM_EVERY
Definition: timeout.h:55
@ TMPARAM_AFTER
Definition: timeout.h:53
const char * type

References disable_alarm, elog, enable_timeout(), ERROR, GetCurrentTimestamp(), i, EnableTimeoutParams::id, now(), schedule_alarm(), TimestampTzPlusMilliseconds, TMPARAM_AFTER, TMPARAM_AT, TMPARAM_EVERY, and type.

Referenced by ProcSleep(), ResolveRecoveryConflictWithBufferPin(), and ResolveRecoveryConflictWithLock().

◆ find_active_timeout()

static int find_active_timeout ( TimeoutId  id)
static

Definition at line 96 of file timeout.c.

97{
98 int i;
99
100 for (i = 0; i < num_active_timeouts; i++)
101 {
102 if (active_timeouts[i]->index == id)
103 return i;
104 }
105
106 return -1;
107}

References active_timeouts, i, and num_active_timeouts.

Referenced by disable_timeout(), disable_timeouts(), and enable_timeout().

◆ get_timeout_active()

bool get_timeout_active ( TimeoutId  id)

◆ get_timeout_finish_time()

TimestampTz get_timeout_finish_time ( TimeoutId  id)

Definition at line 827 of file timeout.c.

828{
829 return all_timeouts[id].fin_time;
830}

References all_timeouts, and timeout_params::fin_time.

Referenced by ProcessInterrupts().

◆ get_timeout_indicator()

bool get_timeout_indicator ( TimeoutId  id,
bool  reset_indicator 
)

Definition at line 793 of file timeout.c.

794{
795 if (all_timeouts[id].indicator)
796 {
797 if (reset_indicator)
798 all_timeouts[id].indicator = false;
799 return true;
800 }
801 return false;
802}

References all_timeouts, and timeout_params::indicator.

Referenced by ProcessInterrupts().

◆ get_timeout_start_time()

TimestampTz get_timeout_start_time ( TimeoutId  id)

Definition at line 813 of file timeout.c.

814{
815 return all_timeouts[id].start_time;
816}

References all_timeouts, and timeout_params::start_time.

Referenced by ProcSleep().

◆ handle_sig_alarm()

static void handle_sig_alarm ( SIGNAL_ARGS  )
static

Definition at line 364 of file timeout.c.

365{
366 /*
367 * Bump the holdoff counter, to make sure nothing we call will process
368 * interrupts directly. No timeout handler should do that, but these
369 * failures are hard to debug, so better be sure.
370 */
372
373 /*
374 * SIGALRM is always cause for waking anything waiting on the process
375 * latch.
376 */
378
379 /*
380 * Always reset signal_pending, even if !alarm_enabled, since indeed no
381 * signal is now pending.
382 */
383 signal_pending = false;
384
385 /*
386 * Fire any pending timeouts, but only if we're enabled to do so.
387 */
388 if (alarm_enabled)
389 {
390 /*
391 * Disable alarms, just in case this platform allows signal handlers
392 * to interrupt themselves. schedule_alarm() will re-enable if
393 * appropriate.
394 */
396
397 if (num_active_timeouts > 0)
398 {
400
401 /* While the first pending timeout has been reached ... */
402 while (num_active_timeouts > 0 &&
403 now >= active_timeouts[0]->fin_time)
404 {
405 timeout_params *this_timeout = active_timeouts[0];
406
407 /* Remove it from the active list */
409
410 /* Mark it as fired */
411 this_timeout->indicator = true;
412
413 /* And call its handler function */
414 this_timeout->timeout_handler();
415
416 /* If it should fire repeatedly, re-enable it. */
417 if (this_timeout->interval_in_ms > 0)
418 {
419 TimestampTz new_fin_time;
420
421 /*
422 * To guard against drift, schedule the next instance of
423 * the timeout based on the intended firing time rather
424 * than the actual firing time. But if the timeout was so
425 * late that we missed an entire cycle, fall back to
426 * scheduling based on the actual firing time.
427 */
428 new_fin_time =
430 this_timeout->interval_in_ms);
431 if (new_fin_time < now)
432 new_fin_time =
434 this_timeout->interval_in_ms);
435 enable_timeout(this_timeout->index, now, new_fin_time,
436 this_timeout->interval_in_ms);
437 }
438
439 /*
440 * The handler might not take negligible time (CheckDeadLock
441 * for instance isn't too cheap), so let's update our idea of
442 * "now" after each one.
443 */
445 }
446
447 /* Done firing timeouts, so reschedule next interrupt if any */
449 }
450 }
451
453}
struct Latch * MyLatch
Definition: globals.c:62
void SetLatch(Latch *latch)
Definition: latch.c:632
#define RESUME_INTERRUPTS()
Definition: miscadmin.h:135
#define HOLD_INTERRUPTS()
Definition: miscadmin.h:133
timeout_handler_proc timeout_handler
Definition: timeout.c:35
TimeoutId index
Definition: timeout.c:28
static volatile sig_atomic_t alarm_enabled
Definition: timeout.c:67
static volatile sig_atomic_t signal_pending
Definition: timeout.c:78

References active_timeouts, alarm_enabled, disable_alarm, enable_timeout(), timeout_params::fin_time, GetCurrentTimestamp(), HOLD_INTERRUPTS, timeout_params::index, timeout_params::indicator, timeout_params::interval_in_ms, MyLatch, now(), num_active_timeouts, remove_timeout_index(), RESUME_INTERRUPTS, schedule_alarm(), SetLatch(), signal_pending, timeout_params::timeout_handler, and TimestampTzPlusMilliseconds.

Referenced by InitializeTimeouts().

◆ InitializeTimeouts()

void InitializeTimeouts ( void  )

Definition at line 470 of file timeout.c.

471{
472 int i;
473
474 /* Initialize, or re-initialize, all local state */
476
478
479 for (i = 0; i < MAX_TIMEOUTS; i++)
480 {
482 all_timeouts[i].active = false;
483 all_timeouts[i].indicator = false;
488 }
489
491
492 /* Now establish the signal handler */
494}
pqsigfunc pqsignal(int signo, pqsigfunc func)
static void handle_sig_alarm(SIGNAL_ARGS)
Definition: timeout.c:364
#define SIGALRM
Definition: win32_port.h:172

References timeout_params::active, all_timeouts, all_timeouts_initialized, disable_alarm, timeout_params::fin_time, handle_sig_alarm(), i, timeout_params::index, timeout_params::indicator, timeout_params::interval_in_ms, MAX_TIMEOUTS, num_active_timeouts, pqsignal(), SIGALRM, timeout_params::start_time, and timeout_params::timeout_handler.

Referenced by AutoVacWorkerMain(), BackendInitialize(), BackgroundWorkerMain(), PostgresMain(), ReplSlotSyncWorkerMain(), StartupProcessMain(), and WalSndSignals().

◆ insert_timeout()

static void insert_timeout ( TimeoutId  id,
int  index 
)
static

Definition at line 114 of file timeout.c.

115{
116 int i;
117
119 elog(FATAL, "timeout index %d out of range 0..%d", index,
121
122 Assert(!all_timeouts[id].active);
123 all_timeouts[id].active = true;
124
125 for (i = num_active_timeouts - 1; i >= index; i--)
127
129
131}
#define FATAL
Definition: elog.h:41

References timeout_params::active, active_timeouts, all_timeouts, Assert, elog, FATAL, i, and num_active_timeouts.

Referenced by enable_timeout().

◆ RegisterTimeout()

TimeoutId RegisterTimeout ( TimeoutId  id,
timeout_handler_proc  handler 
)

Definition at line 505 of file timeout.c.

506{
508
509 /* There's no need to disable the signal handler here. */
510
511 if (id >= USER_TIMEOUT)
512 {
513 /* Allocate a user-defined timeout reason */
514 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
515 if (all_timeouts[id].timeout_handler == NULL)
516 break;
517 if (id >= MAX_TIMEOUTS)
519 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
520 errmsg("cannot add more timeout reasons")));
521 }
522
523 Assert(all_timeouts[id].timeout_handler == NULL);
524
525 all_timeouts[id].timeout_handler = handler;
526
527 return id;
528}
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereport(elevel,...)
Definition: elog.h:149
@ USER_TIMEOUT
Definition: timeout.h:40

References all_timeouts, all_timeouts_initialized, Assert, ereport, errcode(), errmsg(), FATAL, MAX_TIMEOUTS, timeout_params::timeout_handler, and USER_TIMEOUT.

Referenced by BackendInitialize(), InitPostgres(), StartupProcessMain(), and StartupXLOG().

◆ remove_timeout_index()

static void remove_timeout_index ( int  index)
static

Definition at line 137 of file timeout.c.

138{
139 int i;
140
142 elog(FATAL, "timeout index %d out of range 0..%d", index,
144
145 Assert(active_timeouts[index]->active);
146 active_timeouts[index]->active = false;
147
148 for (i = index + 1; i < num_active_timeouts; i++)
150
152}

References timeout_params::active, active_timeouts, Assert, elog, FATAL, i, and num_active_timeouts.

Referenced by disable_timeout(), disable_timeouts(), enable_timeout(), and handle_sig_alarm().

◆ reschedule_timeouts()

void reschedule_timeouts ( void  )

Definition at line 540 of file timeout.c.

541{
542 /* For flexibility, allow this to be called before we're initialized. */
544 return;
545
546 /* Disable timeout interrupts for safety. */
548
549 /* Reschedule the interrupt, if any timeouts remain active. */
550 if (num_active_timeouts > 0)
552}

References all_timeouts_initialized, disable_alarm, GetCurrentTimestamp(), num_active_timeouts, and schedule_alarm().

Referenced by AbortSubTransaction(), and AbortTransaction().

◆ schedule_alarm()

static void schedule_alarm ( TimestampTz  now)
static

Definition at line 210 of file timeout.c.

211{
212 if (num_active_timeouts > 0)
213 {
214 struct itimerval timeval;
215 TimestampTz nearest_timeout;
216 long secs;
217 int usecs;
218
219 MemSet(&timeval, 0, sizeof(struct itimerval));
220
221 /*
222 * If we think there's a signal pending, but current time is more than
223 * 10ms past when the signal was due, then assume that the timeout
224 * request got lost somehow; clear signal_pending so that we'll reset
225 * the interrupt request below. (10ms corresponds to the worst-case
226 * timeout granularity on modern systems.) It won't hurt us if the
227 * interrupt does manage to fire between now and when we reach the
228 * setitimer() call.
229 */
230 if (signal_pending && now > signal_due_at + 10 * 1000)
231 signal_pending = false;
232
233 /*
234 * Get the time remaining till the nearest pending timeout. If it is
235 * negative, assume that we somehow missed an interrupt, and clear
236 * signal_pending. This gives us another chance to recover if the
237 * kernel drops a timeout request for some reason.
238 */
239 nearest_timeout = active_timeouts[0]->fin_time;
240 if (now > nearest_timeout)
241 {
242 signal_pending = false;
243 /* force an interrupt as soon as possible */
244 secs = 0;
245 usecs = 1;
246 }
247 else
248 {
249 TimestampDifference(now, nearest_timeout,
250 &secs, &usecs);
251
252 /*
253 * It's possible that the difference is less than a microsecond;
254 * ensure we don't cancel, rather than set, the interrupt.
255 */
256 if (secs == 0 && usecs == 0)
257 usecs = 1;
258 }
259
260 timeval.it_value.tv_sec = secs;
261 timeval.it_value.tv_usec = usecs;
262
263 /*
264 * We must enable the signal handler before calling setitimer(); if we
265 * did it in the other order, we'd have a race condition wherein the
266 * interrupt could occur before we can set alarm_enabled, so that the
267 * signal handler would fail to do anything.
268 *
269 * Because we didn't bother to disable the timer in disable_alarm(),
270 * it's possible that a previously-set interrupt will fire between
271 * enable_alarm() and setitimer(). This is safe, however. There are
272 * two possible outcomes:
273 *
274 * 1. The signal handler finds nothing to do (because the nearest
275 * timeout event is still in the future). It will re-set the timer
276 * and return. Then we'll overwrite the timer value with a new one.
277 * This will mean that the timer fires a little later than we
278 * intended, but only by the amount of time it takes for the signal
279 * handler to do nothing useful, which shouldn't be much.
280 *
281 * 2. The signal handler executes and removes one or more timeout
282 * events. When it returns, either the queue is now empty or the
283 * frontmost event is later than the one we looked at above. So we'll
284 * overwrite the timer value with one that is too soon (plus or minus
285 * the signal handler's execution time), causing a useless interrupt
286 * to occur. But the handler will then re-set the timer and
287 * everything will still work as expected.
288 *
289 * Since these cases are of very low probability (the window here
290 * being quite narrow), it's not worth adding cycles to the mainline
291 * code to prevent occasional wasted interrupts.
292 */
293 enable_alarm();
294
295 /*
296 * If there is already an interrupt pending that's at or before the
297 * needed time, we need not do anything more. The signal handler will
298 * do the right thing in the first case, and re-schedule the interrupt
299 * for later in the second case. It might seem that the extra
300 * interrupt is wasted work, but it's not terribly much work, and this
301 * method has very significant advantages in the common use-case where
302 * we repeatedly set a timeout that we don't expect to reach and then
303 * cancel it. Instead of invoking setitimer() every time the timeout
304 * is set or canceled, we perform one interrupt and a re-scheduling
305 * setitimer() call at intervals roughly equal to the timeout delay.
306 * For example, with statement_timeout = 1s and a throughput of
307 * thousands of queries per second, this method requires an interrupt
308 * and setitimer() call roughly once a second, rather than thousands
309 * of setitimer() calls per second.
310 *
311 * Because of the possible passage of time between when we obtained
312 * "now" and when we reach setitimer(), the kernel's opinion of when
313 * to trigger the interrupt is likely to be a bit later than
314 * signal_due_at. That's fine, for the same reasons described above.
315 */
316 if (signal_pending && nearest_timeout >= signal_due_at)
317 return;
318
319 /*
320 * As with calling enable_alarm(), we must set signal_pending *before*
321 * calling setitimer(); if we did it after, the signal handler could
322 * trigger before we set it, leaving us with a false opinion that a
323 * signal is still coming.
324 *
325 * Other race conditions involved with setting/checking signal_pending
326 * are okay, for the reasons described above. One additional point is
327 * that the signal handler could fire after we set signal_due_at, but
328 * still before the setitimer() call. Then the handler could
329 * overwrite signal_due_at with a value it computes, which will be the
330 * same as or perhaps later than what we just computed. After we
331 * perform setitimer(), the net effect would be that signal_due_at
332 * gives a time later than when the interrupt will really happen;
333 * which is a safe situation.
334 */
335 signal_due_at = nearest_timeout;
336 signal_pending = true;
337
338 /* Set the alarm timer */
339 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
340 {
341 /*
342 * Clearing signal_pending here is a bit pro forma, but not
343 * entirely so, since something in the FATAL exit path could try
344 * to use timeout facilities.
345 */
346 signal_pending = false;
347 elog(FATAL, "could not enable SIGALRM timer: %m");
348 }
349 }
350}
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition: timestamp.c:1720
#define MemSet(start, val, len)
Definition: c.h:974
#define enable_alarm()
Definition: timeout.c:70
static volatile TimestampTz signal_due_at
Definition: timeout.c:79
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)
Definition: timer.c:86
#define ITIMER_REAL
Definition: win32_port.h:188

References active_timeouts, elog, enable_alarm, FATAL, timeout_params::fin_time, itimerval::it_value, ITIMER_REAL, MemSet, now(), num_active_timeouts, setitimer(), signal_due_at, signal_pending, and TimestampDifference().

Referenced by disable_timeout(), disable_timeouts(), enable_timeout_after(), enable_timeout_at(), enable_timeout_every(), enable_timeouts(), handle_sig_alarm(), and reschedule_timeouts().

Variable Documentation

◆ active_timeouts

◆ alarm_enabled

volatile sig_atomic_t alarm_enabled = false
static

Definition at line 67 of file timeout.c.

Referenced by handle_sig_alarm().

◆ all_timeouts

◆ all_timeouts_initialized

bool all_timeouts_initialized = false
static

◆ num_active_timeouts

◆ signal_due_at

volatile TimestampTz signal_due_at = 0
static

Definition at line 79 of file timeout.c.

Referenced by schedule_alarm().

◆ signal_pending

volatile sig_atomic_t signal_pending = false
static

Definition at line 78 of file timeout.c.

Referenced by handle_sig_alarm(), and schedule_alarm().