LCOV - code coverage report
Current view: top level - src/backend/utils/misc - timeout.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 162 196 82.7 %
Date: 2023-05-31 01:12:27 Functions: 18 20 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * timeout.c
       4             :  *    Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, 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             :     int         interval_in_ms; /* time between firings, or 0 if just once */
      40             : } timeout_params;
      41             : 
      42             : /*
      43             :  * List of possible timeout reasons in the order of enum TimeoutId.
      44             :  */
      45             : static timeout_params all_timeouts[MAX_TIMEOUTS];
      46             : static bool all_timeouts_initialized = false;
      47             : 
      48             : /*
      49             :  * List of active timeouts ordered by their fin_time and priority.
      50             :  * This list is subject to change by the interrupt handler, so it's volatile.
      51             :  */
      52             : static volatile int num_active_timeouts = 0;
      53             : static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
      54             : 
      55             : /*
      56             :  * Flag controlling whether the signal handler is allowed to do anything.
      57             :  * This is useful to avoid race conditions with the handler.  Note in
      58             :  * particular that this lets us make changes in the data structures without
      59             :  * tediously disabling and re-enabling the timer signal.  Most of the time,
      60             :  * no interrupt would happen anyway during such critical sections, but if
      61             :  * one does, this rule ensures it's safe.  Leaving the signal enabled across
      62             :  * multiple operations can greatly reduce the number of kernel calls we make,
      63             :  * too.  See comments in schedule_alarm() about that.
      64             :  *
      65             :  * We leave this "false" when we're not expecting interrupts, just in case.
      66             :  */
      67             : static volatile sig_atomic_t alarm_enabled = false;
      68             : 
      69             : #define disable_alarm() (alarm_enabled = false)
      70             : #define enable_alarm()  (alarm_enabled = true)
      71             : 
      72             : /*
      73             :  * State recording if and when we next expect the interrupt to fire.
      74             :  * (signal_due_at is valid only when signal_pending is true.)
      75             :  * Note that the signal handler will unconditionally reset signal_pending to
      76             :  * false, so that can change asynchronously even when alarm_enabled is false.
      77             :  */
      78             : static volatile sig_atomic_t signal_pending = false;
      79             : static volatile TimestampTz signal_due_at = 0;
      80             : 
      81             : 
      82             : /*****************************************************************************
      83             :  * Internal helper functions
      84             :  *
      85             :  * For all of these, it is caller's responsibility to protect them from
      86             :  * interruption by the signal handler.  Generally, call disable_alarm()
      87             :  * first to prevent interruption, then update state, and last call
      88             :  * schedule_alarm(), which will re-enable the signal handler if needed.
      89             :  *****************************************************************************/
      90             : 
      91             : /*
      92             :  * Find the index of a given timeout reason in the active array.
      93             :  * If it's not there, return -1.
      94             :  */
      95             : static int
      96       41124 : find_active_timeout(TimeoutId id)
      97             : {
      98             :     int         i;
      99             : 
     100       41432 :     for (i = 0; i < num_active_timeouts; i++)
     101             :     {
     102       41432 :         if (active_timeouts[i]->index == id)
     103       41124 :             return i;
     104             :     }
     105             : 
     106           0 :     return -1;
     107             : }
     108             : 
     109             : /*
     110             :  * Insert specified timeout reason into the list of active timeouts
     111             :  * at the given index.
     112             :  */
     113             : static void
     114       94068 : insert_timeout(TimeoutId id, int index)
     115             : {
     116             :     int         i;
     117             : 
     118       94068 :     if (index < 0 || index > num_active_timeouts)
     119           0 :         elog(FATAL, "timeout index %d out of range 0..%d", index,
     120             :              num_active_timeouts);
     121             : 
     122             :     Assert(!all_timeouts[id].active);
     123       94068 :     all_timeouts[id].active = true;
     124             : 
     125       95888 :     for (i = num_active_timeouts - 1; i >= index; i--)
     126        1820 :         active_timeouts[i + 1] = active_timeouts[i];
     127             : 
     128       94068 :     active_timeouts[index] = &all_timeouts[id];
     129             : 
     130       94068 :     num_active_timeouts++;
     131       94068 : }
     132             : 
     133             : /*
     134             :  * Remove the index'th element from the timeout list.
     135             :  */
     136             : static void
     137       41238 : remove_timeout_index(int index)
     138             : {
     139             :     int         i;
     140             : 
     141       41238 :     if (index < 0 || index >= num_active_timeouts)
     142           0 :         elog(FATAL, "timeout index %d out of range 0..%d", index,
     143             :              num_active_timeouts - 1);
     144             : 
     145             :     Assert(active_timeouts[index]->active);
     146       41238 :     active_timeouts[index]->active = false;
     147             : 
     148       43328 :     for (i = index + 1; i < num_active_timeouts; i++)
     149        2090 :         active_timeouts[i - 1] = active_timeouts[i];
     150             : 
     151       41238 :     num_active_timeouts--;
     152       41238 : }
     153             : 
     154             : /*
     155             :  * Enable the specified timeout reason
     156             :  */
     157             : static void
     158       94068 : enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time,
     159             :                int interval_in_ms)
     160             : {
     161             :     int         i;
     162             : 
     163             :     /* Assert request is sane */
     164             :     Assert(all_timeouts_initialized);
     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       94068 :     if (all_timeouts[id].active)
     172           0 :         remove_timeout_index(find_active_timeout(id));
     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       94666 :     for (i = 0; i < num_active_timeouts; i++)
     179             :     {
     180        2398 :         timeout_params *old_timeout = active_timeouts[i];
     181             : 
     182        2398 :         if (fin_time < old_timeout->fin_time)
     183        1800 :             break;
     184         598 :         if (fin_time == old_timeout->fin_time && id < old_timeout->index)
     185           0 :             break;
     186             :     }
     187             : 
     188             :     /*
     189             :      * Mark the timeout active, and insert it into the active list.
     190             :      */
     191       94068 :     all_timeouts[id].indicator = false;
     192       94068 :     all_timeouts[id].start_time = now;
     193       94068 :     all_timeouts[id].fin_time = fin_time;
     194       94068 :     all_timeouts[id].interval_in_ms = interval_in_ms;
     195             : 
     196       94068 :     insert_timeout(id, i);
     197       94068 : }
     198             : 
     199             : /*
     200             :  * Schedule alarm for the next active timeout, if any
     201             :  *
     202             :  * We assume the caller has obtained the current time, or a close-enough
     203             :  * approximation.  (It's okay if a tick or two has passed since "now", or
     204             :  * if a little more time elapses before we reach the kernel call; that will
     205             :  * cause us to ask for an interrupt a tick or two later than the nearest
     206             :  * timeout, which is no big deal.  Passing a "now" value that's in the future
     207             :  * would be bad though.)
     208             :  */
     209             : static void
     210      108058 : schedule_alarm(TimestampTz now)
     211             : {
     212      108058 :     if (num_active_timeouts > 0)
     213             :     {
     214             :         struct itimerval timeval;
     215             :         TimestampTz nearest_timeout;
     216             :         long        secs;
     217             :         int         usecs;
     218             : 
     219      540090 :         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      108018 :         if (signal_pending && now > signal_due_at + 10 * 1000)
     231           0 :             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      108018 :         nearest_timeout = active_timeouts[0]->fin_time;
     240      108018 :         if (now > nearest_timeout)
     241             :         {
     242           0 :             signal_pending = false;
     243             :             /* force an interrupt as soon as possible */
     244           0 :             secs = 0;
     245           0 :             usecs = 1;
     246             :         }
     247             :         else
     248             :         {
     249      108018 :             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      108018 :             if (secs == 0 && usecs == 0)
     257           0 :                 usecs = 1;
     258             :         }
     259             : 
     260      108018 :         timeval.it_value.tv_sec = secs;
     261      108018 :         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      108018 :         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      108018 :         if (signal_pending && nearest_timeout >= signal_due_at)
     317       71038 :             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       36980 :         signal_due_at = nearest_timeout;
     336       36980 :         signal_pending = true;
     337             : 
     338             :         /* Set the alarm timer */
     339       36980 :         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           0 :             signal_pending = false;
     347           0 :             elog(FATAL, "could not enable SIGALRM timer: %m");
     348             :         }
     349             :     }
     350             : }
     351             : 
     352             : 
     353             : /*****************************************************************************
     354             :  * Signal handler
     355             :  *****************************************************************************/
     356             : 
     357             : /*
     358             :  * Signal handler for SIGALRM
     359             :  *
     360             :  * Process any active timeout reasons and then reschedule the interrupt
     361             :  * as needed.
     362             :  */
     363             : static void
     364         234 : handle_sig_alarm(SIGNAL_ARGS)
     365             : {
     366         234 :     int         save_errno = errno;
     367             : 
     368             :     /*
     369             :      * Bump the holdoff counter, to make sure nothing we call will process
     370             :      * interrupts directly. No timeout handler should do that, but these
     371             :      * failures are hard to debug, so better be sure.
     372             :      */
     373         234 :     HOLD_INTERRUPTS();
     374             : 
     375             :     /*
     376             :      * SIGALRM is always cause for waking anything waiting on the process
     377             :      * latch.
     378             :      */
     379         234 :     SetLatch(MyLatch);
     380             : 
     381             :     /*
     382             :      * Always reset signal_pending, even if !alarm_enabled, since indeed no
     383             :      * signal is now pending.
     384             :      */
     385         234 :     signal_pending = false;
     386             : 
     387             :     /*
     388             :      * Fire any pending timeouts, but only if we're enabled to do so.
     389             :      */
     390         234 :     if (alarm_enabled)
     391             :     {
     392             :         /*
     393             :          * Disable alarms, just in case this platform allows signal handlers
     394             :          * to interrupt themselves.  schedule_alarm() will re-enable if
     395             :          * appropriate.
     396             :          */
     397         184 :         disable_alarm();
     398             : 
     399         184 :         if (num_active_timeouts > 0)
     400             :         {
     401         184 :             TimestampTz now = GetCurrentTimestamp();
     402             : 
     403             :             /* While the first pending timeout has been reached ... */
     404         298 :             while (num_active_timeouts > 0 &&
     405         258 :                    now >= active_timeouts[0]->fin_time)
     406             :             {
     407         114 :                 timeout_params *this_timeout = active_timeouts[0];
     408             : 
     409             :                 /* Remove it from the active list */
     410         114 :                 remove_timeout_index(0);
     411             : 
     412             :                 /* Mark it as fired */
     413         114 :                 this_timeout->indicator = true;
     414             : 
     415             :                 /* And call its handler function */
     416         114 :                 this_timeout->timeout_handler();
     417             : 
     418             :                 /* If it should fire repeatedly, re-enable it. */
     419         114 :                 if (this_timeout->interval_in_ms > 0)
     420             :                 {
     421             :                     TimestampTz new_fin_time;
     422             : 
     423             :                     /*
     424             :                      * To guard against drift, schedule the next instance of
     425             :                      * the timeout based on the intended firing time rather
     426             :                      * than the actual firing time. But if the timeout was so
     427             :                      * late that we missed an entire cycle, fall back to
     428             :                      * scheduling based on the actual firing time.
     429             :                      */
     430          12 :                     new_fin_time =
     431          12 :                         TimestampTzPlusMilliseconds(this_timeout->fin_time,
     432             :                                                     this_timeout->interval_in_ms);
     433          12 :                     if (new_fin_time < now)
     434           0 :                         new_fin_time =
     435           0 :                             TimestampTzPlusMilliseconds(now,
     436             :                                                         this_timeout->interval_in_ms);
     437          12 :                     enable_timeout(this_timeout->index, now, new_fin_time,
     438             :                                    this_timeout->interval_in_ms);
     439             :                 }
     440             : 
     441             :                 /*
     442             :                  * The handler might not take negligible time (CheckDeadLock
     443             :                  * for instance isn't too cheap), so let's update our idea of
     444             :                  * "now" after each one.
     445             :                  */
     446         114 :                 now = GetCurrentTimestamp();
     447             :             }
     448             : 
     449             :             /* Done firing timeouts, so reschedule next interrupt if any */
     450         184 :             schedule_alarm(now);
     451             :         }
     452             :     }
     453             : 
     454         234 :     RESUME_INTERRUPTS();
     455             : 
     456         234 :     errno = save_errno;
     457         234 : }
     458             : 
     459             : 
     460             : /*****************************************************************************
     461             :  * Public API
     462             :  *****************************************************************************/
     463             : 
     464             : /*
     465             :  * Initialize timeout module.
     466             :  *
     467             :  * This must be called in every process that wants to use timeouts.
     468             :  *
     469             :  * If the process was forked from another one that was also using this
     470             :  * module, be sure to call this before re-enabling signals; else handlers
     471             :  * meant to run in the parent process might get invoked in this one.
     472             :  */
     473             : void
     474       43048 : InitializeTimeouts(void)
     475             : {
     476             :     int         i;
     477             : 
     478             :     /* Initialize, or re-initialize, all local state */
     479       43048 :     disable_alarm();
     480             : 
     481       43048 :     num_active_timeouts = 0;
     482             : 
     483      990104 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     484             :     {
     485      947056 :         all_timeouts[i].index = i;
     486      947056 :         all_timeouts[i].active = false;
     487      947056 :         all_timeouts[i].indicator = false;
     488      947056 :         all_timeouts[i].timeout_handler = NULL;
     489      947056 :         all_timeouts[i].start_time = 0;
     490      947056 :         all_timeouts[i].fin_time = 0;
     491      947056 :         all_timeouts[i].interval_in_ms = 0;
     492             :     }
     493             : 
     494       43048 :     all_timeouts_initialized = true;
     495             : 
     496             :     /* Now establish the signal handler */
     497       43048 :     pqsignal(SIGALRM, handle_sig_alarm);
     498       43048 : }
     499             : 
     500             : /*
     501             :  * Register a timeout reason
     502             :  *
     503             :  * For predefined timeouts, this just registers the callback function.
     504             :  *
     505             :  * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
     506             :  * return a timeout ID.
     507             :  */
     508             : TimeoutId
     509      187774 : RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
     510             : {
     511             :     Assert(all_timeouts_initialized);
     512             : 
     513             :     /* There's no need to disable the signal handler here. */
     514             : 
     515      187774 :     if (id >= USER_TIMEOUT)
     516             :     {
     517             :         /* Allocate a user-defined timeout reason */
     518           0 :         for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
     519           0 :             if (all_timeouts[id].timeout_handler == NULL)
     520           0 :                 break;
     521           0 :         if (id >= MAX_TIMEOUTS)
     522           0 :             ereport(FATAL,
     523             :                     (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
     524             :                      errmsg("cannot add more timeout reasons")));
     525             :     }
     526             : 
     527             :     Assert(all_timeouts[id].timeout_handler == NULL);
     528             : 
     529      187774 :     all_timeouts[id].timeout_handler = handler;
     530             : 
     531      187774 :     return id;
     532             : }
     533             : 
     534             : /*
     535             :  * Reschedule any pending SIGALRM interrupt.
     536             :  *
     537             :  * This can be used during error recovery in case query cancel resulted in loss
     538             :  * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it
     539             :  * could do anything).  But note it's not necessary if any of the public
     540             :  * enable_ or disable_timeout functions are called in the same area, since
     541             :  * those all do schedule_alarm() internally if needed.
     542             :  */
     543             : void
     544       49268 : reschedule_timeouts(void)
     545             : {
     546             :     /* For flexibility, allow this to be called before we're initialized. */
     547       49268 :     if (!all_timeouts_initialized)
     548           0 :         return;
     549             : 
     550             :     /* Disable timeout interrupts for safety. */
     551       49268 :     disable_alarm();
     552             : 
     553             :     /* Reschedule the interrupt, if any timeouts remain active. */
     554       49268 :     if (num_active_timeouts > 0)
     555       12328 :         schedule_alarm(GetCurrentTimestamp());
     556             : }
     557             : 
     558             : /*
     559             :  * Enable the specified timeout to fire after the specified delay.
     560             :  *
     561             :  * Delay is given in milliseconds.
     562             :  */
     563             : void
     564       92726 : enable_timeout_after(TimeoutId id, int delay_ms)
     565             : {
     566             :     TimestampTz now;
     567             :     TimestampTz fin_time;
     568             : 
     569             :     /* Disable timeout interrupts for safety. */
     570       92726 :     disable_alarm();
     571             : 
     572             :     /* Queue the timeout at the appropriate time. */
     573       92726 :     now = GetCurrentTimestamp();
     574       92726 :     fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
     575       92726 :     enable_timeout(id, now, fin_time, 0);
     576             : 
     577             :     /* Set the timer interrupt. */
     578       92726 :     schedule_alarm(now);
     579       92726 : }
     580             : 
     581             : /*
     582             :  * Enable the specified timeout to fire periodically, with the specified
     583             :  * delay as the time between firings.
     584             :  *
     585             :  * Delay is given in milliseconds.
     586             :  */
     587             : void
     588         722 : enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
     589             : {
     590             :     TimestampTz now;
     591             : 
     592             :     /* Disable timeout interrupts for safety. */
     593         722 :     disable_alarm();
     594             : 
     595             :     /* Queue the timeout at the appropriate time. */
     596         722 :     now = GetCurrentTimestamp();
     597         722 :     enable_timeout(id, now, fin_time, delay_ms);
     598             : 
     599             :     /* Set the timer interrupt. */
     600         722 :     schedule_alarm(now);
     601         722 : }
     602             : 
     603             : /*
     604             :  * Enable the specified timeout to fire at the specified time.
     605             :  *
     606             :  * This is provided to support cases where there's a reason to calculate
     607             :  * the timeout by reference to some point other than "now".  If there isn't,
     608             :  * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
     609             :  */
     610             : void
     611           0 : enable_timeout_at(TimeoutId id, TimestampTz fin_time)
     612             : {
     613             :     TimestampTz now;
     614             : 
     615             :     /* Disable timeout interrupts for safety. */
     616           0 :     disable_alarm();
     617             : 
     618             :     /* Queue the timeout at the appropriate time. */
     619           0 :     now = GetCurrentTimestamp();
     620           0 :     enable_timeout(id, now, fin_time, 0);
     621             : 
     622             :     /* Set the timer interrupt. */
     623           0 :     schedule_alarm(now);
     624           0 : }
     625             : 
     626             : /*
     627             :  * Enable multiple timeouts at once.
     628             :  *
     629             :  * This works like calling enable_timeout_after() and/or enable_timeout_at()
     630             :  * multiple times.  Use this to reduce the number of GetCurrentTimestamp()
     631             :  * and setitimer() calls needed to establish multiple timeouts.
     632             :  */
     633             : void
     634         304 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
     635             : {
     636             :     TimestampTz now;
     637             :     int         i;
     638             : 
     639             :     /* Disable timeout interrupts for safety. */
     640         304 :     disable_alarm();
     641             : 
     642             :     /* Queue the timeout(s) at the appropriate times. */
     643         304 :     now = GetCurrentTimestamp();
     644             : 
     645         912 :     for (i = 0; i < count; i++)
     646             :     {
     647         608 :         TimeoutId   id = timeouts[i].id;
     648             :         TimestampTz fin_time;
     649             : 
     650         608 :         switch (timeouts[i].type)
     651             :         {
     652         582 :             case TMPARAM_AFTER:
     653         582 :                 fin_time = TimestampTzPlusMilliseconds(now,
     654             :                                                        timeouts[i].delay_ms);
     655         582 :                 enable_timeout(id, now, fin_time, 0);
     656         582 :                 break;
     657             : 
     658          26 :             case TMPARAM_AT:
     659          26 :                 enable_timeout(id, now, timeouts[i].fin_time, 0);
     660          26 :                 break;
     661             : 
     662           0 :             case TMPARAM_EVERY:
     663           0 :                 fin_time = TimestampTzPlusMilliseconds(now,
     664             :                                                        timeouts[i].delay_ms);
     665           0 :                 enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
     666           0 :                 break;
     667             : 
     668           0 :             default:
     669           0 :                 elog(ERROR, "unrecognized timeout type %d",
     670             :                      (int) timeouts[i].type);
     671             :                 break;
     672             :         }
     673             :     }
     674             : 
     675             :     /* Set the timer interrupt. */
     676         304 :     schedule_alarm(now);
     677         304 : }
     678             : 
     679             : /*
     680             :  * Cancel the specified timeout.
     681             :  *
     682             :  * The timeout's I've-been-fired indicator is reset,
     683             :  * unless keep_indicator is true.
     684             :  *
     685             :  * When a timeout is canceled, any other active timeout remains in force.
     686             :  * It's not an error to disable a timeout that is not enabled.
     687             :  */
     688             : void
     689       41002 : disable_timeout(TimeoutId id, bool keep_indicator)
     690             : {
     691             :     /* Assert request is sane */
     692             :     Assert(all_timeouts_initialized);
     693             :     Assert(all_timeouts[id].timeout_handler != NULL);
     694             : 
     695             :     /* Disable timeout interrupts for safety. */
     696       41002 :     disable_alarm();
     697             : 
     698             :     /* Find the timeout and remove it from the active list. */
     699       41002 :     if (all_timeouts[id].active)
     700       40514 :         remove_timeout_index(find_active_timeout(id));
     701             : 
     702             :     /* Mark it inactive, whether it was active or not. */
     703       41002 :     if (!keep_indicator)
     704       41002 :         all_timeouts[id].indicator = false;
     705             : 
     706             :     /* Reschedule the interrupt, if any timeouts remain active. */
     707       41002 :     if (num_active_timeouts > 0)
     708        1464 :         schedule_alarm(GetCurrentTimestamp());
     709       41002 : }
     710             : 
     711             : /*
     712             :  * Cancel multiple timeouts at once.
     713             :  *
     714             :  * The timeouts' I've-been-fired indicators are reset,
     715             :  * unless timeouts[i].keep_indicator is true.
     716             :  *
     717             :  * This works like calling disable_timeout() multiple times.
     718             :  * Use this to reduce the number of GetCurrentTimestamp()
     719             :  * and setitimer() calls needed to cancel multiple timeouts.
     720             :  */
     721             : void
     722         346 : disable_timeouts(const DisableTimeoutParams *timeouts, int count)
     723             : {
     724             :     int         i;
     725             : 
     726             :     Assert(all_timeouts_initialized);
     727             : 
     728             :     /* Disable timeout interrupts for safety. */
     729         346 :     disable_alarm();
     730             : 
     731             :     /* Cancel the timeout(s). */
     732        1038 :     for (i = 0; i < count; i++)
     733             :     {
     734         692 :         TimeoutId   id = timeouts[i].id;
     735             : 
     736             :         Assert(all_timeouts[id].timeout_handler != NULL);
     737             : 
     738         692 :         if (all_timeouts[id].active)
     739         610 :             remove_timeout_index(find_active_timeout(id));
     740             : 
     741         692 :         if (!timeouts[i].keep_indicator)
     742         346 :             all_timeouts[id].indicator = false;
     743             :     }
     744             : 
     745             :     /* Reschedule the interrupt, if any timeouts remain active. */
     746         346 :     if (num_active_timeouts > 0)
     747         330 :         schedule_alarm(GetCurrentTimestamp());
     748         346 : }
     749             : 
     750             : /*
     751             :  * Disable the signal handler, remove all timeouts from the active list,
     752             :  * and optionally reset their timeout indicators.
     753             :  */
     754             : void
     755       35412 : disable_all_timeouts(bool keep_indicators)
     756             : {
     757             :     int         i;
     758             : 
     759       35412 :     disable_alarm();
     760             : 
     761             :     /*
     762             :      * We used to disable the timer interrupt here, but in common usage
     763             :      * patterns it's cheaper to leave it enabled; that may save us from having
     764             :      * to enable it again shortly.  See comments in schedule_alarm().
     765             :      */
     766             : 
     767       35412 :     num_active_timeouts = 0;
     768             : 
     769      814476 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     770             :     {
     771      779064 :         all_timeouts[i].active = false;
     772      779064 :         if (!keep_indicators)
     773      779064 :             all_timeouts[i].indicator = false;
     774             :     }
     775       35412 : }
     776             : 
     777             : /*
     778             :  * Return true if the timeout is active (enabled and not yet fired)
     779             :  *
     780             :  * This is, of course, subject to race conditions, as the timeout could fire
     781             :  * immediately after we look.
     782             :  */
     783             : bool
     784     4572074 : get_timeout_active(TimeoutId id)
     785             : {
     786     4572074 :     return all_timeouts[id].active;
     787             : }
     788             : 
     789             : /*
     790             :  * Return the timeout's I've-been-fired indicator
     791             :  *
     792             :  * If reset_indicator is true, reset the indicator when returning true.
     793             :  * To avoid missing timeouts due to race conditions, we are careful not to
     794             :  * reset the indicator when returning false.
     795             :  */
     796             : bool
     797         188 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
     798             : {
     799         188 :     if (all_timeouts[id].indicator)
     800             :     {
     801          18 :         if (reset_indicator)
     802          18 :             all_timeouts[id].indicator = false;
     803          18 :         return true;
     804             :     }
     805         170 :     return false;
     806             : }
     807             : 
     808             : /*
     809             :  * Return the time when the timeout was most recently activated
     810             :  *
     811             :  * Note: will return 0 if timeout has never been activated in this process.
     812             :  * However, we do *not* reset the start_time when a timeout occurs, so as
     813             :  * not to create a race condition if SIGALRM fires just as some code is
     814             :  * about to fetch the value.
     815             :  */
     816             : TimestampTz
     817        2026 : get_timeout_start_time(TimeoutId id)
     818             : {
     819        2026 :     return all_timeouts[id].start_time;
     820             : }
     821             : 
     822             : /*
     823             :  * Return the time when the timeout is, or most recently was, due to fire
     824             :  *
     825             :  * Note: will return 0 if timeout has never been activated in this process.
     826             :  * However, we do *not* reset the fin_time when a timeout occurs, so as
     827             :  * not to create a race condition if SIGALRM fires just as some code is
     828             :  * about to fetch the value.
     829             :  */
     830             : TimestampTz
     831           0 : get_timeout_finish_time(TimeoutId id)
     832             : {
     833           0 :     return all_timeouts[id].fin_time;
     834             : }

Generated by: LCOV version 1.14