LCOV - code coverage report
Current view: top level - src/backend/utils/misc - timeout.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 82.5 % 194 160
Test Date: 2026-03-03 03:15:11 Functions: 90.0 % 20 18
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-2026, 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        31224 : find_active_timeout(TimeoutId id)
      97              : {
      98              :     int         i;
      99              : 
     100        31361 :     for (i = 0; i < num_active_timeouts; i++)
     101              :     {
     102        31361 :         if (active_timeouts[i]->index == id)
     103        31224 :             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        66856 : insert_timeout(TimeoutId id, int index)
     115              : {
     116              :     int         i;
     117              : 
     118        66856 :     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        66856 :     all_timeouts[id].active = true;
     124              : 
     125        68039 :     for (i = num_active_timeouts - 1; i >= index; i--)
     126         1183 :         active_timeouts[i + 1] = active_timeouts[i];
     127              : 
     128        66856 :     active_timeouts[index] = &all_timeouts[id];
     129              : 
     130        66856 :     num_active_timeouts++;
     131        66856 : }
     132              : 
     133              : /*
     134              :  * Remove the index'th element from the timeout list.
     135              :  */
     136              : static void
     137        31318 : remove_timeout_index(int index)
     138              : {
     139              :     int         i;
     140              : 
     141        31318 :     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        31318 :     active_timeouts[index]->active = false;
     147              : 
     148        32614 :     for (i = index + 1; i < num_active_timeouts; i++)
     149         1296 :         active_timeouts[i - 1] = active_timeouts[i];
     150              : 
     151        31318 :     num_active_timeouts--;
     152        31318 : }
     153              : 
     154              : /*
     155              :  * Enable the specified timeout reason
     156              :  */
     157              : static void
     158        66856 : 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        66856 :     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        67114 :     for (i = 0; i < num_active_timeouts; i++)
     179              :     {
     180         1431 :         timeout_params *old_timeout = active_timeouts[i];
     181              : 
     182         1431 :         if (fin_time < old_timeout->fin_time)
     183         1173 :             break;
     184          258 :         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        66856 :     all_timeouts[id].indicator = false;
     192        66856 :     all_timeouts[id].start_time = now;
     193        66856 :     all_timeouts[id].fin_time = fin_time;
     194        66856 :     all_timeouts[id].interval_in_ms = interval_in_ms;
     195              : 
     196        66856 :     insert_timeout(id, i);
     197        66856 : }
     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        75504 : schedule_alarm(TimestampTz now)
     211              : {
     212        75504 :     if (num_active_timeouts > 0)
     213              :     {
     214              :         struct itimerval timeval;
     215              :         TimestampTz nearest_timeout;
     216              :         long        secs;
     217              :         int         usecs;
     218              : 
     219       377360 :         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        75472 :         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        75472 :         nearest_timeout = active_timeouts[0]->fin_time;
     240        75472 :         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        75472 :             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        75472 :             if (secs == 0 && usecs == 0)
     257            0 :                 usecs = 1;
     258              :         }
     259              : 
     260        75472 :         timeval.it_value.tv_sec = secs;
     261        75472 :         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        75472 :         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        75472 :         if (signal_pending && nearest_timeout >= signal_due_at)
     317        47709 :             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        27763 :         signal_due_at = nearest_timeout;
     336        27763 :         signal_pending = true;
     337              : 
     338              :         /* Set the alarm timer */
     339        27763 :         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          253 : 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          253 :     HOLD_INTERRUPTS();
     372              : 
     373              :     /*
     374              :      * SIGALRM is always cause for waking anything waiting on the process
     375              :      * latch.
     376              :      */
     377          253 :     SetLatch(MyLatch);
     378              : 
     379              :     /*
     380              :      * Always reset signal_pending, even if !alarm_enabled, since indeed no
     381              :      * signal is now pending.
     382              :      */
     383          253 :     signal_pending = false;
     384              : 
     385              :     /*
     386              :      * Fire any pending timeouts, but only if we're enabled to do so.
     387              :      */
     388          253 :     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          202 :         disable_alarm();
     396              : 
     397          202 :         if (num_active_timeouts > 0)
     398              :         {
     399          202 :             TimestampTz now = GetCurrentTimestamp();
     400              : 
     401              :             /* While the first pending timeout has been reached ... */
     402          296 :             while (num_active_timeouts > 0 &&
     403          264 :                    now >= active_timeouts[0]->fin_time)
     404              :             {
     405           94 :                 timeout_params *this_timeout = active_timeouts[0];
     406              : 
     407              :                 /* Remove it from the active list */
     408           94 :                 remove_timeout_index(0);
     409              : 
     410              :                 /* Mark it as fired */
     411           94 :                 this_timeout->indicator = true;
     412              : 
     413              :                 /* And call its handler function */
     414           94 :                 this_timeout->timeout_handler();
     415              : 
     416              :                 /* If it should fire repeatedly, re-enable it. */
     417           94 :                 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           26 :                     new_fin_time =
     429           26 :                         TimestampTzPlusMilliseconds(this_timeout->fin_time,
     430              :                                                     this_timeout->interval_in_ms);
     431           26 :                     if (new_fin_time < now)
     432            0 :                         new_fin_time =
     433            0 :                             TimestampTzPlusMilliseconds(now,
     434              :                                                         this_timeout->interval_in_ms);
     435           26 :                     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           94 :                 now = GetCurrentTimestamp();
     445              :             }
     446              : 
     447              :             /* Done firing timeouts, so reschedule next interrupt if any */
     448          202 :             schedule_alarm(now);
     449              :         }
     450              :     }
     451              : 
     452          253 :     RESUME_INTERRUPTS();
     453          253 : }
     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        33271 : InitializeTimeouts(void)
     471              : {
     472              :     int         i;
     473              : 
     474              :     /* Initialize, or re-initialize, all local state */
     475        33271 :     disable_alarm();
     476              : 
     477        33271 :     num_active_timeouts = 0;
     478              : 
     479       798504 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     480              :     {
     481       765233 :         all_timeouts[i].index = i;
     482       765233 :         all_timeouts[i].active = false;
     483       765233 :         all_timeouts[i].indicator = false;
     484       765233 :         all_timeouts[i].timeout_handler = NULL;
     485       765233 :         all_timeouts[i].start_time = 0;
     486       765233 :         all_timeouts[i].fin_time = 0;
     487       765233 :         all_timeouts[i].interval_in_ms = 0;
     488              :     }
     489              : 
     490        33271 :     all_timeouts_initialized = true;
     491              : 
     492              :     /* Now establish the signal handler */
     493        33271 :     pqsignal(SIGALRM, handle_sig_alarm);
     494        33271 : }
     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       164232 : 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       164232 :     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       164232 :     all_timeouts[id].timeout_handler = handler;
     526              : 
     527       164232 :     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        31295 : reschedule_timeouts(void)
     541              : {
     542              :     /* For flexibility, allow this to be called before we're initialized. */
     543        31295 :     if (!all_timeouts_initialized)
     544            0 :         return;
     545              : 
     546              :     /* Disable timeout interrupts for safety. */
     547        31295 :     disable_alarm();
     548              : 
     549              :     /* Reschedule the interrupt, if any timeouts remain active. */
     550        31295 :     if (num_active_timeouts > 0)
     551         7418 :         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        66082 : enable_timeout_after(TimeoutId id, int delay_ms)
     561              : {
     562              :     TimestampTz now;
     563              :     TimestampTz fin_time;
     564              : 
     565              :     /* Disable timeout interrupts for safety. */
     566        66082 :     disable_alarm();
     567              : 
     568              :     /* Queue the timeout at the appropriate time. */
     569        66082 :     now = GetCurrentTimestamp();
     570        66082 :     fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
     571        66082 :     enable_timeout(id, now, fin_time, 0);
     572              : 
     573              :     /* Set the timer interrupt. */
     574        66082 :     schedule_alarm(now);
     575        66082 : }
     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          496 : enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
     585              : {
     586              :     TimestampTz now;
     587              : 
     588              :     /* Disable timeout interrupts for safety. */
     589          496 :     disable_alarm();
     590              : 
     591              :     /* Queue the timeout at the appropriate time. */
     592          496 :     now = GetCurrentTimestamp();
     593          496 :     enable_timeout(id, now, fin_time, delay_ms);
     594              : 
     595              :     /* Set the timer interrupt. */
     596          496 :     schedule_alarm(now);
     597          496 : }
     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          126 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
     631              : {
     632              :     TimestampTz now;
     633              :     int         i;
     634              : 
     635              :     /* Disable timeout interrupts for safety. */
     636          126 :     disable_alarm();
     637              : 
     638              :     /* Queue the timeout(s) at the appropriate times. */
     639          126 :     now = GetCurrentTimestamp();
     640              : 
     641          378 :     for (i = 0; i < count; i++)
     642              :     {
     643          252 :         TimeoutId   id = timeouts[i].id;
     644              :         TimestampTz fin_time;
     645              : 
     646          252 :         switch (timeouts[i].type)
     647              :         {
     648          241 :             case TMPARAM_AFTER:
     649          241 :                 fin_time = TimestampTzPlusMilliseconds(now,
     650              :                                                        timeouts[i].delay_ms);
     651          241 :                 enable_timeout(id, now, fin_time, 0);
     652          241 :                 break;
     653              : 
     654           11 :             case TMPARAM_AT:
     655           11 :                 enable_timeout(id, now, timeouts[i].fin_time, 0);
     656           11 :                 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          126 :     schedule_alarm(now);
     673          126 : }
     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        31331 : 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        31331 :     disable_alarm();
     693              : 
     694              :     /* Find the timeout and remove it from the active list. */
     695        31331 :     if (all_timeouts[id].active)
     696        30967 :         remove_timeout_index(find_active_timeout(id));
     697              : 
     698              :     /* Mark it inactive, whether it was active or not. */
     699        31331 :     if (!keep_indicator)
     700        31331 :         all_timeouts[id].indicator = false;
     701              : 
     702              :     /* Reschedule the interrupt, if any timeouts remain active. */
     703        31331 :     if (num_active_timeouts > 0)
     704         1039 :         schedule_alarm(GetCurrentTimestamp());
     705        31331 : }
     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          149 : 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          149 :     disable_alarm();
     726              : 
     727              :     /* Cancel the timeout(s). */
     728          447 :     for (i = 0; i < count; i++)
     729              :     {
     730          298 :         TimeoutId   id = timeouts[i].id;
     731              : 
     732              :         Assert(all_timeouts[id].timeout_handler != NULL);
     733              : 
     734          298 :         if (all_timeouts[id].active)
     735          257 :             remove_timeout_index(find_active_timeout(id));
     736              : 
     737          298 :         if (!timeouts[i].keep_indicator)
     738          149 :             all_timeouts[id].indicator = false;
     739              :     }
     740              : 
     741              :     /* Reschedule the interrupt, if any timeouts remain active. */
     742          149 :     if (num_active_timeouts > 0)
     743          141 :         schedule_alarm(GetCurrentTimestamp());
     744          149 : }
     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        22797 : disable_all_timeouts(bool keep_indicators)
     752              : {
     753              :     int         i;
     754              : 
     755        22797 :     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        22797 :     num_active_timeouts = 0;
     764              : 
     765       547128 :     for (i = 0; i < MAX_TIMEOUTS; i++)
     766              :     {
     767       524331 :         all_timeouts[i].active = false;
     768       524331 :         if (!keep_indicators)
     769       524331 :             all_timeouts[i].indicator = false;
     770              :     }
     771        22797 : }
     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      1774704 : get_timeout_active(TimeoutId id)
     781              : {
     782      1774704 :     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          114 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
     794              : {
     795          114 :     if (all_timeouts[id].indicator)
     796              :     {
     797           10 :         if (reset_indicator)
     798           10 :             all_timeouts[id].indicator = false;
     799           10 :         return true;
     800              :     }
     801          104 :     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         1673 : get_timeout_start_time(TimeoutId id)
     814              : {
     815         1673 :     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 2.0-1