1/*-------------------------------------------------------------------------
2 *
3 * timeout.c
4 * Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
5 *
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/backend/utils/misc/timeout.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include <sys/time.h>
18
19#include "miscadmin.h"
20#include "storage/proc.h"
21#include "utils/timeout.h"
22#include "utils/timestamp.h"
23
24
25/* Data about any one timeout reason */
26typedef struct timeout_params
27{
28 TimeoutId index; /* identifier of timeout reason */
29
30 /* volatile because it may be changed from the signal handler */
31 volatile bool indicator; /* true if timeout has occurred */
32
33 /* callback function for timeout, or NULL if timeout not registered */
34 timeout_handler_proc timeout_handler;
35
36 TimestampTz start_time; /* time that timeout was last activated */
37 TimestampTz fin_time; /* time it is, or was last, due to fire */
38} timeout_params;
39
40/*
41 * List of possible timeout reasons in the order of enum TimeoutId.
42 */
43static timeout_params all_timeouts[MAX_TIMEOUTS];
44static bool all_timeouts_initialized = false;
45
46/*
47 * List of active timeouts ordered by their fin_time and priority.
48 * This list is subject to change by the interrupt handler, so it's volatile.
49 */
50static volatile int num_active_timeouts = 0;
51static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
52
53/*
54 * Flag controlling whether the signal handler is allowed to do anything.
55 * We leave this "false" when we're not expecting interrupts, just in case.
56 *
57 * Note that we don't bother to reset any pending timer interrupt when we
58 * disable the signal handler; it's not really worth the cycles to do so,
59 * since the probability of the interrupt actually occurring while we have
60 * it disabled is low. See comments in schedule_alarm() about that.
61 */
62static volatile sig_atomic_t alarm_enabled = false;
63
64#define disable_alarm() (alarm_enabled = false)
65#define enable_alarm() (alarm_enabled = true)
66
67
68/*****************************************************************************
69 * Internal helper functions
70 *
71 * For all of these, it is caller's responsibility to protect them from
72 * interruption by the signal handler. Generally, call disable_alarm()
73 * first to prevent interruption, then update state, and last call
74 * schedule_alarm(), which will re-enable the signal handler if needed.
75 *****************************************************************************/
76
77/*
78 * Find the index of a given timeout reason in the active array.
79 * If it's not there, return -1.
80 */
81static int
82find_active_timeout(TimeoutId id)
83{
84 int i;
85
86 for (i = 0; i < num_active_timeouts; i++)
87 {
88 if (active_timeouts[i]->index == id)
89 return i;
90 }
91
92 return -1;
93}
94
95/*
96 * Insert specified timeout reason into the list of active timeouts
97 * at the given index.
98 */
99static void
100insert_timeout(TimeoutId id, int index)
101{
102 int i;
103
104 if (index < 0 || index > num_active_timeouts)
105 elog(FATAL, "timeout index %d out of range 0..%d", index,
106 num_active_timeouts);
107
108 for (i = num_active_timeouts - 1; i >= index; i--)
109 active_timeouts[i + 1] = active_timeouts[i];
110
111 active_timeouts[index] = &all_timeouts[id];
112
113 num_active_timeouts++;
114}
115
116/*
117 * Remove the index'th element from the timeout list.
118 */
119static void
120remove_timeout_index(int index)
121{
122 int i;
123
124 if (index < 0 || index >= num_active_timeouts)
125 elog(FATAL, "timeout index %d out of range 0..%d", index,
126 num_active_timeouts - 1);
127
128 for (i = index + 1; i < num_active_timeouts; i++)
129 active_timeouts[i - 1] = active_timeouts[i];
130
131 num_active_timeouts--;
132}
133
134/*
135 * Enable the specified timeout reason
136 */
137static void
138enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time)
139{
140 int i;
141
142 /* Assert request is sane */
143 Assert(all_timeouts_initialized);
144 Assert(all_timeouts[id].timeout_handler != NULL);
145
146 /*
147 * If this timeout was already active, momentarily disable it. We
148 * interpret the call as a directive to reschedule the timeout.
149 */
150 i = find_active_timeout(id);
151 if (i >= 0)
152 remove_timeout_index(i);
153
154 /*
155 * Find out the index where to insert the new timeout. We sort by
156 * fin_time, and for equal fin_time by priority.
157 */
158 for (i = 0; i < num_active_timeouts; i++)
159 {
160 timeout_params *old_timeout = active_timeouts[i];
161
162 if (fin_time < old_timeout->fin_time)
163 break;
164 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
165 break;
166 }
167
168 /*
169 * Mark the timeout active, and insert it into the active list.
170 */
171 all_timeouts[id].indicator = false;
172 all_timeouts[id].start_time = now;
173 all_timeouts[id].fin_time = fin_time;
174
175 insert_timeout(id, i);
176}
177
178/*
179 * Schedule alarm for the next active timeout, if any
180 *
181 * We assume the caller has obtained the current time, or a close-enough
182 * approximation.
183 */
184static void
185schedule_alarm(TimestampTz now)
186{
187 if (num_active_timeouts > 0)
188 {
189 struct itimerval timeval;
190 long secs;
191 int usecs;
192
193 MemSet(&timeval, 0, sizeof(struct itimerval));
194
195 /* Get the time remaining till the nearest pending timeout */
196 TimestampDifference(now, active_timeouts[0]->fin_time,
197 &secs, &usecs);
198
199 /*
200 * It's possible that the difference is less than a microsecond;
201 * ensure we don't cancel, rather than set, the interrupt.
202 */
203 if (secs == 0 && usecs == 0)
204 usecs = 1;
205
206 timeval.it_value.tv_sec = secs;
207 timeval.it_value.tv_usec = usecs;
208
209 /*
210 * We must enable the signal handler before calling setitimer(); if we
211 * did it in the other order, we'd have a race condition wherein the
212 * interrupt could occur before we can set alarm_enabled, so that the
213 * signal handler would fail to do anything.
214 *
215 * Because we didn't bother to reset the timer in disable_alarm(),
216 * it's possible that a previously-set interrupt will fire between
217 * enable_alarm() and setitimer(). This is safe, however. There are
218 * two possible outcomes:
219 *
220 * 1. The signal handler finds nothing to do (because the nearest
221 * timeout event is still in the future). It will re-set the timer
222 * and return. Then we'll overwrite the timer value with a new one.
223 * This will mean that the timer fires a little later than we
224 * intended, but only by the amount of time it takes for the signal
225 * handler to do nothing useful, which shouldn't be much.
226 *
227 * 2. The signal handler executes and removes one or more timeout
228 * events. When it returns, either the queue is now empty or the
229 * frontmost event is later than the one we looked at above. So we'll
230 * overwrite the timer value with one that is too soon (plus or minus
231 * the signal handler's execution time), causing a useless interrupt
232 * to occur. But the handler will then re-set the timer and
233 * everything will still work as expected.
234 *
235 * Since these cases are of very low probability (the window here
236 * being quite narrow), it's not worth adding cycles to the mainline
237 * code to prevent occasional wasted interrupts.
238 */
239 enable_alarm();
240
241 /* Set the alarm timer */
242 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
243 elog(FATAL, "could not enable SIGALRM timer: %m");
244 }
245}
246
247
248/*****************************************************************************
249 * Signal handler
250 *****************************************************************************/
251
252/*
253 * Signal handler for SIGALRM
254 *
255 * Process any active timeout reasons and then reschedule the interrupt
256 * as needed.
257 */
258static void
259handle_sig_alarm(SIGNAL_ARGS)
260{
261 int save_errno = errno;
262
263 /*
264 * Bump the holdoff counter, to make sure nothing we call will process
265 * interrupts directly. No timeout handler should do that, but these
266 * failures are hard to debug, so better be sure.
267 */
268 HOLD_INTERRUPTS();
269
270 /*
271 * SIGALRM is always cause for waking anything waiting on the process
272 * latch.
273 */
274 SetLatch(MyLatch);
275
276 /*
277 * Fire any pending timeouts, but only if we're enabled to do so.
278 */
279 if (alarm_enabled)
280 {
281 /*
282 * Disable alarms, just in case this platform allows signal handlers
283 * to interrupt themselves. schedule_alarm() will re-enable if
284 * appropriate.
285 */
286 disable_alarm();
287
288 if (num_active_timeouts > 0)
289 {
290 TimestampTz now = GetCurrentTimestamp();
291
292 /* While the first pending timeout has been reached ... */
293 while (num_active_timeouts > 0 &&
294 now >= active_timeouts[0]->fin_time)
295 {
296 timeout_params *this_timeout = active_timeouts[0];
297
298 /* Remove it from the active list */
299 remove_timeout_index(0);
300
301 /* Mark it as fired */
302 this_timeout->indicator = true;
303
304 /* And call its handler function */
305 this_timeout->timeout_handler();
306
307 /*
308 * The handler might not take negligible time (CheckDeadLock
309 * for instance isn't too cheap), so let's update our idea of
310 * "now" after each one.
311 */
312 now = GetCurrentTimestamp();
313 }
314
315 /* Done firing timeouts, so reschedule next interrupt if any */
316 schedule_alarm(now);
317 }
318 }
319
320 RESUME_INTERRUPTS();
321
322 errno = save_errno;
323}
324
325
326/*****************************************************************************
327 * Public API
328 *****************************************************************************/
329
330/*
331 * Initialize timeout module.
332 *
333 * This must be called in every process that wants to use timeouts.
334 *
335 * If the process was forked from another one that was also using this
336 * module, be sure to call this before re-enabling signals; else handlers
337 * meant to run in the parent process might get invoked in this one.
338 */
339void
340InitializeTimeouts(void)
341{
342 int i;
343
344 /* Initialize, or re-initialize, all local state */
345 disable_alarm();
346
347 num_active_timeouts = 0;
348
349 for (i = 0; i < MAX_TIMEOUTS; i++)
350 {
351 all_timeouts[i].index = i;
352 all_timeouts[i].indicator = false;
353 all_timeouts[i].timeout_handler = NULL;
354 all_timeouts[i].start_time = 0;
355 all_timeouts[i].fin_time = 0;
356 }
357
358 all_timeouts_initialized = true;
359
360 /* Now establish the signal handler */
361 pqsignal(SIGALRM, handle_sig_alarm);
362}
363
364/*
365 * Register a timeout reason
366 *
367 * For predefined timeouts, this just registers the callback function.
368 *
369 * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
370 * return a timeout ID.
371 */
372TimeoutId
373RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
374{
375 Assert(all_timeouts_initialized);
376
377 /* There's no need to disable the signal handler here. */
378
379 if (id >= USER_TIMEOUT)
380 {
381 /* Allocate a user-defined timeout reason */
382 for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
383 if (all_timeouts[id].timeout_handler == NULL)
384 break;
385 if (id >= MAX_TIMEOUTS)
386 ereport(FATAL,
387 (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
388 errmsg("cannot add more timeout reasons")));
389 }
390
391 Assert(all_timeouts[id].timeout_handler == NULL);
392
393 all_timeouts[id].timeout_handler = handler;
394
395 return id;
396}
397
398/*
399 * Reschedule any pending SIGALRM interrupt.
400 *
401 * This can be used during error recovery in case query cancel resulted in loss
402 * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it
403 * could do anything). But note it's not necessary if any of the public
404 * enable_ or disable_timeout functions are called in the same area, since
405 * those all do schedule_alarm() internally if needed.
406 */
407void
408reschedule_timeouts(void)
409{
410 /* For flexibility, allow this to be called before we're initialized. */
411 if (!all_timeouts_initialized)
412 return;
413
414 /* Disable timeout interrupts for safety. */
415 disable_alarm();
416
417 /* Reschedule the interrupt, if any timeouts remain active. */
418 if (num_active_timeouts > 0)
419 schedule_alarm(GetCurrentTimestamp());
420}
421
422/*
423 * Enable the specified timeout to fire after the specified delay.
424 *
425 * Delay is given in milliseconds.
426 */
427void
428enable_timeout_after(TimeoutId id, int delay_ms)
429{
430 TimestampTz now;
431 TimestampTz fin_time;
432
433 /* Disable timeout interrupts for safety. */
434 disable_alarm();
435
436 /* Queue the timeout at the appropriate time. */
437 now = GetCurrentTimestamp();
438 fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
439 enable_timeout(id, now, fin_time);
440
441 /* Set the timer interrupt. */
442 schedule_alarm(now);
443}
444
445/*
446 * Enable the specified timeout to fire at the specified time.
447 *
448 * This is provided to support cases where there's a reason to calculate
449 * the timeout by reference to some point other than "now". If there isn't,
450 * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
451 */
452void
453enable_timeout_at(TimeoutId id, TimestampTz fin_time)
454{
455 TimestampTz now;
456
457 /* Disable timeout interrupts for safety. */
458 disable_alarm();
459
460 /* Queue the timeout at the appropriate time. */
461 now = GetCurrentTimestamp();
462 enable_timeout(id, now, fin_time);
463
464 /* Set the timer interrupt. */
465 schedule_alarm(now);
466}
467
468/*
469 * Enable multiple timeouts at once.
470 *
471 * This works like calling enable_timeout_after() and/or enable_timeout_at()
472 * multiple times. Use this to reduce the number of GetCurrentTimestamp()
473 * and setitimer() calls needed to establish multiple timeouts.
474 */
475void
476enable_timeouts(const EnableTimeoutParams *timeouts, int count)
477{
478 TimestampTz now;
479 int i;
480
481 /* Disable timeout interrupts for safety. */
482 disable_alarm();
483
484 /* Queue the timeout(s) at the appropriate times. */
485 now = GetCurrentTimestamp();
486
487 for (i = 0; i < count; i++)
488 {
489 TimeoutId id = timeouts[i].id;
490 TimestampTz fin_time;
491
492 switch (timeouts[i].type)
493 {
494 case TMPARAM_AFTER:
495 fin_time = TimestampTzPlusMilliseconds(now,
496 timeouts[i].delay_ms);
497 enable_timeout(id, now, fin_time);
498 break;
499
500 case TMPARAM_AT:
501 enable_timeout(id, now, timeouts[i].fin_time);
502 break;
503
504 default:
505 elog(ERROR, "unrecognized timeout type %d",
506 (int) timeouts[i].type);
507 break;
508 }
509 }
510
511 /* Set the timer interrupt. */
512 schedule_alarm(now);
513}
514
515/*
516 * Cancel the specified timeout.
517 *
518 * The timeout's I've-been-fired indicator is reset,
519 * unless keep_indicator is true.
520 *
521 * When a timeout is canceled, any other active timeout remains in force.
522 * It's not an error to disable a timeout that is not enabled.
523 */
524void
525disable_timeout(TimeoutId id, bool keep_indicator)
526{
527 int i;
528
529 /* Assert request is sane */
530 Assert(all_timeouts_initialized);
531 Assert(all_timeouts[id].timeout_handler != NULL);
532
533 /* Disable timeout interrupts for safety. */
534 disable_alarm();
535
536 /* Find the timeout and remove it from the active list. */
537 i = find_active_timeout(id);
538 if (i >= 0)
539 remove_timeout_index(i);
540
541 /* Mark it inactive, whether it was active or not. */
542 if (!keep_indicator)
543 all_timeouts[id].indicator = false;
544
545 /* Reschedule the interrupt, if any timeouts remain active. */
546 if (num_active_timeouts > 0)
547 schedule_alarm(GetCurrentTimestamp());
548}
549
550/*
551 * Cancel multiple timeouts at once.
552 *
553 * The timeouts' I've-been-fired indicators are reset,
554 * unless timeouts[i].keep_indicator is true.
555 *
556 * This works like calling disable_timeout() multiple times.
557 * Use this to reduce the number of GetCurrentTimestamp()
558 * and setitimer() calls needed to cancel multiple timeouts.
559 */
560void
561disable_timeouts(const DisableTimeoutParams *timeouts, int count)
562{
563 int i;
564
565 Assert(all_timeouts_initialized);
566
567 /* Disable timeout interrupts for safety. */
568 disable_alarm();
569
570 /* Cancel the timeout(s). */
571 for (i = 0; i < count; i++)
572 {
573 TimeoutId id = timeouts[i].id;
574 int idx;
575
576 Assert(all_timeouts[id].timeout_handler != NULL);
577
578 idx = find_active_timeout(id);
579 if (idx >= 0)
580 remove_timeout_index(idx);
581
582 if (!timeouts[i].keep_indicator)
583 all_timeouts[id].indicator = false;
584 }
585
586 /* Reschedule the interrupt, if any timeouts remain active. */
587 if (num_active_timeouts > 0)
588 schedule_alarm(GetCurrentTimestamp());
589}
590
591/*
592 * Disable SIGALRM and remove all timeouts from the active list,
593 * and optionally reset their timeout indicators.
594 */
595void
596disable_all_timeouts(bool keep_indicators)
597{
598 disable_alarm();
599
600 /*
601 * Only bother to reset the timer if we think it's active. We could just
602 * let the interrupt happen anyway, but it's probably a bit cheaper to do
603 * setitimer() than to let the useless interrupt happen.
604 */
605 if (num_active_timeouts > 0)
606 {
607 struct itimerval timeval;
608
609 MemSet(&timeval, 0, sizeof(struct itimerval));
610 if (setitimer(ITIMER_REAL, &timeval, NULL) != 0)
611 elog(FATAL, "could not disable SIGALRM timer: %m");
612 }
613
614 num_active_timeouts = 0;
615
616 if (!keep_indicators)
617 {
618 int i;
619
620 for (i = 0; i < MAX_TIMEOUTS; i++)
621 all_timeouts[i].indicator = false;
622 }
623}
624
625/*
626 * Return the timeout's I've-been-fired indicator
627 *
628 * If reset_indicator is true, reset the indicator when returning true.
629 * To avoid missing timeouts due to race conditions, we are careful not to
630 * reset the indicator when returning false.
631 */
632bool
633get_timeout_indicator(TimeoutId id, bool reset_indicator)
634{
635 if (all_timeouts[id].indicator)
636 {
637 if (reset_indicator)
638 all_timeouts[id].indicator = false;
639 return true;
640 }
641 return false;
642}
643
644/*
645 * Return the time when the timeout was most recently activated
646 *
647 * Note: will return 0 if timeout has never been activated in this process.
648 * However, we do *not* reset the start_time when a timeout occurs, so as
649 * not to create a race condition if SIGALRM fires just as some code is
650 * about to fetch the value.
651 */
652TimestampTz
653get_timeout_start_time(TimeoutId id)
654{
655 return all_timeouts[id].start_time;
656}
657
658/*
659 * Return the time when the timeout is, or most recently was, due to fire
660 *
661 * Note: will return 0 if timeout has never been activated in this process.
662 * However, we do *not* reset the fin_time when a timeout occurs, so as
663 * not to create a race condition if SIGALRM fires just as some code is
664 * about to fetch the value.
665 */
666TimestampTz
667get_timeout_finish_time(TimeoutId id)
668{
669 return all_timeouts[id].fin_time;
670}
671