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

Generated by: LCOV version 1.13