LCOV - code coverage report
Current view: top level - src/backend/utils/misc - timeout.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 160 194 82.5 %
Date: 2024-04-26 12:11:42 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-2024, 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/latch.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       46384 : find_active_timeout(TimeoutId id)
      97             : {
      98             :     int         i;
      99             : 
     100       46692 :     for (i = 0; i < num_active_timeouts; i++)
     101             :     {
     102       46692 :         if (active_timeouts[i]->index == id)
     103       46384 :             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      105958 : insert_timeout(TimeoutId id, int index)
     115             : {
     116             :     int         i;
     117             : 
     118      105958 :     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      105958 :     all_timeouts[id].active = true;
     124             : 
     125      107866 :     for (i = num_active_timeouts - 1; i >= index; i--)
     126        1908 :         active_timeouts[i + 1] = active_timeouts[i];
     127             : 
     128      105958 :     active_timeouts[index] = &all_timeouts[id];
     129             : 
     130      105958 :     num_active_timeouts++;
     131      105958 : }
     132             : 
     133             : /*
     134             :  * Remove the index'th element from the timeout list.
     135             :  */
     136             : static void
     137       46496 : remove_timeout_index(int index)
     138             : {
     139             :     int         i;
     140             : 
     141       46496 :     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       46496 :     active_timeouts[index]->active = false;
     147             : 
     148       48678 :     for (i = index + 1; i < num_active_timeouts; i++)
     149        2182 :         active_timeouts[i - 1] = active_timeouts[i];
     150             : 
     151       46496 :     num_active_timeouts--;
     152       46496 : }
     153             : 
     154             : /*
     155             :  * Enable the specified timeout reason
     156             :  */
     157             : static void
     158      105958 : 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      105958 :     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      106556 :     for (i = 0; i < num_active_timeouts; i++)
     179             :     {
     180        2486 :         timeout_params *old_timeout = active_timeouts[i];
     181             : 
     182        2486 :         if (fin_time < old_timeout->fin_time)
     183        1888 :             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      105958 :     all_timeouts[id].indicator = false;
     192      105958 :     all_timeouts[id].start_time = now;
     193      105958 :     all_timeouts[id].fin_time = fin_time;
     194      105958 :     all_timeouts[id].interval_in_ms = interval_in_ms;
     195             : 
     196      105958 :     insert_timeout(id, i);
     197      105958 : }
     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      120422 : schedule_alarm(TimestampTz now)
     211             : {
     212      120422 :     if (num_active_timeouts > 0)
     213             :     {
     214             :         struct itimerval timeval;
     215             :         TimestampTz nearest_timeout;
     216             :         long        secs;
     217             :         int         usecs;
     218             : 
     219      601910 :         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      120382 :         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      120382 :         nearest_timeout = active_timeouts[0]->fin_time;
     240      120382 :         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      120382 :             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      120382 :             if (secs == 0 && usecs == 0)
     257           0 :                 usecs = 1;
     258             :         }
     259             : 
     260      120382 :         timeval.it_value.tv_sec = secs;
     261      120382 :         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      120382 :         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      120382 :         if (signal_pending && nearest_timeout >= signal_due_at)
     317       78550 :             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       41832 :         signal_due_at = nearest_timeout;
     336       41832 :         signal_pending = true;
     337             : 
     338             :         /* Set the alarm timer */
     339       41832 :         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         266 : handle_sig_alarm(SIGNAL_ARGS)
     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             :      */
     371         266 :     HOLD_INTERRUPTS();
     372             : 
     373             :     /*
     374             :      * SIGALRM is always cause for waking anything waiting on the process
     375             :      * latch.
     376             :      */
     377         266 :     SetLatch(MyLatch);
     378             : 
     379             :     /*
     380             :      * Always reset signal_pending, even if !alarm_enabled, since indeed no
     381             :      * signal is now pending.
     382             :      */
     383         266 :     signal_pending = false;
     384             : 
     385             :     /*
     386             :      * Fire any pending timeouts, but only if we're enabled to do so.
     387             :      */
     388         266 :     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             :          */
     395         214 :         disable_alarm();
     396             : 
     397         214 :         if (num_active_timeouts > 0)
     398             :         {
     399         214 :             TimestampTz now = GetCurrentTimestamp();
     400             : 
     401             :             /* While the first pending timeout has been reached ... */
     402         326 :             while (num_active_timeouts > 0 &&
     403         286 :                    now >= active_timeouts[0]->fin_time)
     404             :             {
     405         112 :                 timeout_params *this_timeout = active_timeouts[0];
     406             : 
     407             :                 /* Remove it from the active list */
     408         112 :                 remove_timeout_index(0);
     409             : 
     410             :                 /* Mark it as fired */
     411         112 :                 this_timeout->indicator = true;
     412             : 
     413             :                 /* And call its handler function */
     414         112 :                 this_timeout->timeout_handler();
     415             : 
     416             :                 /* If it should fire repeatedly, re-enable it. */
     417         112 :                 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          12 :                     new_fin_time =
     429          12 :                         TimestampTzPlusMilliseconds(this_timeout->fin_time,
     430             :                                                     this_timeout->interval_in_ms);
     431          12 :                     if (new_fin_time < now)
     432           0 :                         new_fin_time =
     433           0 :                             TimestampTzPlusMilliseconds(now,
     434             :                                                         this_timeout->interval_in_ms);
     435          12 :                     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             :                  */
     444         112 :                 now = GetCurrentTimestamp();
     445             :             }
     446             : 
     447             :             /* Done firing timeouts, so reschedule next interrupt if any */
     448         214 :             schedule_alarm(now);
     449             :         }
     450             :     }
     451             : 
     452         266 :     RESUME_INTERRUPTS();
     453         266 : }
     454             : 
     455             : 
     456             : /*****************************************************************************
     457             :  * Public API
     458             :  *****************************************************************************/
     459             : 
     460             : /*
     461             :  * Initialize timeout module.
     462             :  *
     463             :  * This must be called in every process that wants to use timeouts.
     464             :  *
     465             :  * If the process was forked from another one that was also using this
     466             :  * module, be sure to call this before re-enabling signals; else handlers
     467             :  * meant to run in the parent process might get invoked in this one.
     468             :  */
     469             : void
     470       48456 : InitializeTimeouts(void)
     471             : {
     472             :     int         i;
     473             : 
     474             :     /* Initialize, or re-initialize, all local state */
     475       48456 :     disable_alarm();
     476             : 
     477       48456 :     num_active_timeouts = 0;
     478             : 
     479     1162944 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     480             :     {
     481     1114488 :         all_timeouts[i].index = i;
     482     1114488 :         all_timeouts[i].active = false;
     483     1114488 :         all_timeouts[i].indicator = false;
     484     1114488 :         all_timeouts[i].timeout_handler = NULL;
     485     1114488 :         all_timeouts[i].start_time = 0;
     486     1114488 :         all_timeouts[i].fin_time = 0;
     487     1114488 :         all_timeouts[i].interval_in_ms = 0;
     488             :     }
     489             : 
     490       48456 :     all_timeouts_initialized = true;
     491             : 
     492             :     /* Now establish the signal handler */
     493       48456 :     pqsignal(SIGALRM, handle_sig_alarm);
     494       48456 : }
     495             : 
     496             : /*
     497             :  * Register a timeout reason
     498             :  *
     499             :  * For predefined timeouts, this just registers the callback function.
     500             :  *
     501             :  * For user-defined timeouts, pass id == USER_TIMEOUT; we then allocate and
     502             :  * return a timeout ID.
     503             :  */
     504             : TimeoutId
     505      233358 : RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
     506             : {
     507             :     Assert(all_timeouts_initialized);
     508             : 
     509             :     /* There's no need to disable the signal handler here. */
     510             : 
     511      233358 :     if (id >= USER_TIMEOUT)
     512             :     {
     513             :         /* Allocate a user-defined timeout reason */
     514           0 :         for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
     515           0 :             if (all_timeouts[id].timeout_handler == NULL)
     516           0 :                 break;
     517           0 :         if (id >= MAX_TIMEOUTS)
     518           0 :             ereport(FATAL,
     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      233358 :     all_timeouts[id].timeout_handler = handler;
     526             : 
     527      233358 :     return id;
     528             : }
     529             : 
     530             : /*
     531             :  * Reschedule any pending SIGALRM interrupt.
     532             :  *
     533             :  * This can be used during error recovery in case query cancel resulted in loss
     534             :  * of a SIGALRM event (due to longjmp'ing out of handle_sig_alarm before it
     535             :  * could do anything).  But note it's not necessary if any of the public
     536             :  * enable_ or disable_timeout functions are called in the same area, since
     537             :  * those all do schedule_alarm() internally if needed.
     538             :  */
     539             : void
     540       54326 : reschedule_timeouts(void)
     541             : {
     542             :     /* For flexibility, allow this to be called before we're initialized. */
     543       54326 :     if (!all_timeouts_initialized)
     544           0 :         return;
     545             : 
     546             :     /* Disable timeout interrupts for safety. */
     547       54326 :     disable_alarm();
     548             : 
     549             :     /* Reschedule the interrupt, if any timeouts remain active. */
     550       54326 :     if (num_active_timeouts > 0)
     551       12682 :         schedule_alarm(GetCurrentTimestamp());
     552             : }
     553             : 
     554             : /*
     555             :  * Enable the specified timeout to fire after the specified delay.
     556             :  *
     557             :  * Delay is given in milliseconds.
     558             :  */
     559             : void
     560      104454 : enable_timeout_after(TimeoutId id, int delay_ms)
     561             : {
     562             :     TimestampTz now;
     563             :     TimestampTz fin_time;
     564             : 
     565             :     /* Disable timeout interrupts for safety. */
     566      104454 :     disable_alarm();
     567             : 
     568             :     /* Queue the timeout at the appropriate time. */
     569      104454 :     now = GetCurrentTimestamp();
     570      104454 :     fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
     571      104454 :     enable_timeout(id, now, fin_time, 0);
     572             : 
     573             :     /* Set the timer interrupt. */
     574      104454 :     schedule_alarm(now);
     575      104454 : }
     576             : 
     577             : /*
     578             :  * Enable the specified timeout to fire periodically, with the specified
     579             :  * delay as the time between firings.
     580             :  *
     581             :  * Delay is given in milliseconds.
     582             :  */
     583             : void
     584         888 : enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
     585             : {
     586             :     TimestampTz now;
     587             : 
     588             :     /* Disable timeout interrupts for safety. */
     589         888 :     disable_alarm();
     590             : 
     591             :     /* Queue the timeout at the appropriate time. */
     592         888 :     now = GetCurrentTimestamp();
     593         888 :     enable_timeout(id, now, fin_time, delay_ms);
     594             : 
     595             :     /* Set the timer interrupt. */
     596         888 :     schedule_alarm(now);
     597         888 : }
     598             : 
     599             : /*
     600             :  * Enable the specified timeout to fire at the specified time.
     601             :  *
     602             :  * This is provided to support cases where there's a reason to calculate
     603             :  * the timeout by reference to some point other than "now".  If there isn't,
     604             :  * use enable_timeout_after(), to avoid calling GetCurrentTimestamp() twice.
     605             :  */
     606             : void
     607           0 : enable_timeout_at(TimeoutId id, TimestampTz fin_time)
     608             : {
     609             :     TimestampTz now;
     610             : 
     611             :     /* Disable timeout interrupts for safety. */
     612           0 :     disable_alarm();
     613             : 
     614             :     /* Queue the timeout at the appropriate time. */
     615           0 :     now = GetCurrentTimestamp();
     616           0 :     enable_timeout(id, now, fin_time, 0);
     617             : 
     618             :     /* Set the timer interrupt. */
     619           0 :     schedule_alarm(now);
     620           0 : }
     621             : 
     622             : /*
     623             :  * Enable multiple timeouts at once.
     624             :  *
     625             :  * This works like calling enable_timeout_after() and/or enable_timeout_at()
     626             :  * multiple times.  Use this to reduce the number of GetCurrentTimestamp()
     627             :  * and setitimer() calls needed to establish multiple timeouts.
     628             :  */
     629             : void
     630         302 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
     631             : {
     632             :     TimestampTz now;
     633             :     int         i;
     634             : 
     635             :     /* Disable timeout interrupts for safety. */
     636         302 :     disable_alarm();
     637             : 
     638             :     /* Queue the timeout(s) at the appropriate times. */
     639         302 :     now = GetCurrentTimestamp();
     640             : 
     641         906 :     for (i = 0; i < count; i++)
     642             :     {
     643         604 :         TimeoutId   id = timeouts[i].id;
     644             :         TimestampTz fin_time;
     645             : 
     646         604 :         switch (timeouts[i].type)
     647             :         {
     648         580 :             case TMPARAM_AFTER:
     649         580 :                 fin_time = TimestampTzPlusMilliseconds(now,
     650             :                                                        timeouts[i].delay_ms);
     651         580 :                 enable_timeout(id, now, fin_time, 0);
     652         580 :                 break;
     653             : 
     654          24 :             case TMPARAM_AT:
     655          24 :                 enable_timeout(id, now, timeouts[i].fin_time, 0);
     656          24 :                 break;
     657             : 
     658           0 :             case TMPARAM_EVERY:
     659           0 :                 fin_time = TimestampTzPlusMilliseconds(now,
     660             :                                                        timeouts[i].delay_ms);
     661           0 :                 enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
     662           0 :                 break;
     663             : 
     664           0 :             default:
     665           0 :                 elog(ERROR, "unrecognized timeout type %d",
     666             :                      (int) timeouts[i].type);
     667             :                 break;
     668             :         }
     669             :     }
     670             : 
     671             :     /* Set the timer interrupt. */
     672         302 :     schedule_alarm(now);
     673         302 : }
     674             : 
     675             : /*
     676             :  * Cancel the specified timeout.
     677             :  *
     678             :  * The timeout's I've-been-fired indicator is reset,
     679             :  * unless keep_indicator is true.
     680             :  *
     681             :  * When a timeout is canceled, any other active timeout remains in force.
     682             :  * It's not an error to disable a timeout that is not enabled.
     683             :  */
     684             : void
     685       46378 : disable_timeout(TimeoutId id, bool keep_indicator)
     686             : {
     687             :     /* Assert request is sane */
     688             :     Assert(all_timeouts_initialized);
     689             :     Assert(all_timeouts[id].timeout_handler != NULL);
     690             : 
     691             :     /* Disable timeout interrupts for safety. */
     692       46378 :     disable_alarm();
     693             : 
     694             :     /* Find the timeout and remove it from the active list. */
     695       46378 :     if (all_timeouts[id].active)
     696       45770 :         remove_timeout_index(find_active_timeout(id));
     697             : 
     698             :     /* Mark it inactive, whether it was active or not. */
     699       46378 :     if (!keep_indicator)
     700       46378 :         all_timeouts[id].indicator = false;
     701             : 
     702             :     /* Reschedule the interrupt, if any timeouts remain active. */
     703       46378 :     if (num_active_timeouts > 0)
     704        1550 :         schedule_alarm(GetCurrentTimestamp());
     705       46378 : }
     706             : 
     707             : /*
     708             :  * Cancel multiple timeouts at once.
     709             :  *
     710             :  * The timeouts' I've-been-fired indicators are reset,
     711             :  * unless timeouts[i].keep_indicator is true.
     712             :  *
     713             :  * This works like calling disable_timeout() multiple times.
     714             :  * Use this to reduce the number of GetCurrentTimestamp()
     715             :  * and setitimer() calls needed to cancel multiple timeouts.
     716             :  */
     717             : void
     718         348 : disable_timeouts(const DisableTimeoutParams *timeouts, int count)
     719             : {
     720             :     int         i;
     721             : 
     722             :     Assert(all_timeouts_initialized);
     723             : 
     724             :     /* Disable timeout interrupts for safety. */
     725         348 :     disable_alarm();
     726             : 
     727             :     /* Cancel the timeout(s). */
     728        1044 :     for (i = 0; i < count; i++)
     729             :     {
     730         696 :         TimeoutId   id = timeouts[i].id;
     731             : 
     732             :         Assert(all_timeouts[id].timeout_handler != NULL);
     733             : 
     734         696 :         if (all_timeouts[id].active)
     735         614 :             remove_timeout_index(find_active_timeout(id));
     736             : 
     737         696 :         if (!timeouts[i].keep_indicator)
     738         348 :             all_timeouts[id].indicator = false;
     739             :     }
     740             : 
     741             :     /* Reschedule the interrupt, if any timeouts remain active. */
     742         348 :     if (num_active_timeouts > 0)
     743         332 :         schedule_alarm(GetCurrentTimestamp());
     744         348 : }
     745             : 
     746             : /*
     747             :  * Disable the signal handler, remove all timeouts from the active list,
     748             :  * and optionally reset their timeout indicators.
     749             :  */
     750             : void
     751       39986 : disable_all_timeouts(bool keep_indicators)
     752             : {
     753             :     int         i;
     754             : 
     755       39986 :     disable_alarm();
     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             : 
     763       39986 :     num_active_timeouts = 0;
     764             : 
     765      959664 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     766             :     {
     767      919678 :         all_timeouts[i].active = false;
     768      919678 :         if (!keep_indicators)
     769      919678 :             all_timeouts[i].indicator = false;
     770             :     }
     771       39986 : }
     772             : 
     773             : /*
     774             :  * Return true if the timeout is active (enabled and not yet fired)
     775             :  *
     776             :  * This is, of course, subject to race conditions, as the timeout could fire
     777             :  * immediately after we look.
     778             :  */
     779             : bool
     780     2930212 : get_timeout_active(TimeoutId id)
     781             : {
     782     2930212 :     return all_timeouts[id].active;
     783             : }
     784             : 
     785             : /*
     786             :  * Return the timeout's I've-been-fired indicator
     787             :  *
     788             :  * If reset_indicator is true, reset the indicator when returning true.
     789             :  * To avoid missing timeouts due to race conditions, we are careful not to
     790             :  * reset the indicator when returning false.
     791             :  */
     792             : bool
     793         216 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
     794             : {
     795         216 :     if (all_timeouts[id].indicator)
     796             :     {
     797          20 :         if (reset_indicator)
     798          20 :             all_timeouts[id].indicator = false;
     799          20 :         return true;
     800             :     }
     801         196 :     return false;
     802             : }
     803             : 
     804             : /*
     805             :  * Return the time when the timeout was most recently activated
     806             :  *
     807             :  * Note: will return 0 if timeout has never been activated in this process.
     808             :  * However, we do *not* reset the start_time when a timeout occurs, so as
     809             :  * not to create a race condition if SIGALRM fires just as some code is
     810             :  * about to fetch the value.
     811             :  */
     812             : TimestampTz
     813        2172 : get_timeout_start_time(TimeoutId id)
     814             : {
     815        2172 :     return all_timeouts[id].start_time;
     816             : }
     817             : 
     818             : /*
     819             :  * Return the time when the timeout is, or most recently was, due to fire
     820             :  *
     821             :  * Note: will return 0 if timeout has never been activated in this process.
     822             :  * However, we do *not* reset the fin_time when a timeout occurs, so as
     823             :  * not to create a race condition if SIGALRM fires just as some code is
     824             :  * about to fetch the value.
     825             :  */
     826             : TimestampTz
     827           0 : get_timeout_finish_time(TimeoutId id)
     828             : {
     829           0 :     return all_timeouts[id].fin_time;
     830             : }

Generated by: LCOV version 1.14