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

Generated by: LCOV version 1.13