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