LCOV - code coverage report
Current view: top level - src/backend/utils/misc - timeout.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 68.2 % 239 163
Test Date: 2026-01-26 10:56:24 Functions: 75.0 % 20 15
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 51.3 % 152 78

             Branch data     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                 :         730 : find_active_timeout(TimeoutId id)
      97                 :             : {
      98                 :         730 :         int                     i;
      99                 :             : 
     100         [ +  - ]:         732 :         for (i = 0; i < num_active_timeouts; i++)
     101                 :             :         {
     102         [ +  + ]:         732 :                 if (active_timeouts[i]->index == id)
     103                 :         730 :                         return i;
     104                 :           2 :         }
     105                 :             : 
     106                 :           0 :         return -1;
     107                 :         730 : }
     108                 :             : 
     109                 :             : /*
     110                 :             :  * Insert specified timeout reason into the list of active timeouts
     111                 :             :  * at the given index.
     112                 :             :  */
     113                 :             : static void
     114                 :        7747 : insert_timeout(TimeoutId id, int index)
     115                 :             : {
     116                 :        7747 :         int                     i;
     117                 :             : 
     118         [ +  - ]:        7747 :         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         [ +  - ]:        7747 :         Assert(!all_timeouts[id].active);
     123                 :        7747 :         all_timeouts[id].active = true;
     124                 :             : 
     125         [ +  + ]:        7789 :         for (i = num_active_timeouts - 1; i >= index; i--)
     126                 :          42 :                 active_timeouts[i + 1] = active_timeouts[i];
     127                 :             : 
     128                 :        7747 :         active_timeouts[index] = &all_timeouts[id];
     129                 :             : 
     130                 :        7747 :         num_active_timeouts++;
     131                 :        7747 : }
     132                 :             : 
     133                 :             : /*
     134                 :             :  * Remove the index'th element from the timeout list.
     135                 :             :  */
     136                 :             : static void
     137                 :         733 : remove_timeout_index(int index)
     138                 :             : {
     139                 :         733 :         int                     i;
     140                 :             : 
     141         [ +  - ]:         733 :         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         [ +  - ]:         733 :         Assert(active_timeouts[index]->active);
     146                 :         733 :         active_timeouts[index]->active = false;
     147                 :             : 
     148         [ +  + ]:         775 :         for (i = index + 1; i < num_active_timeouts; i++)
     149                 :          42 :                 active_timeouts[i - 1] = active_timeouts[i];
     150                 :             : 
     151                 :         733 :         num_active_timeouts--;
     152                 :         733 : }
     153                 :             : 
     154                 :             : /*
     155                 :             :  * Enable the specified timeout reason
     156                 :             :  */
     157                 :             : static void
     158                 :        7747 : enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time,
     159                 :             :                            int interval_in_ms)
     160                 :             : {
     161                 :        7747 :         int                     i;
     162                 :             : 
     163                 :             :         /* Assert request is sane */
     164         [ +  - ]:        7747 :         Assert(all_timeouts_initialized);
     165         [ +  - ]:        7747 :         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         [ +  - ]:        7747 :         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         [ +  + ]:        7749 :         for (i = 0; i < num_active_timeouts; i++)
     179                 :             :         {
     180                 :          44 :                 timeout_params *old_timeout = active_timeouts[i];
     181                 :             : 
     182         [ +  + ]:          44 :                 if (fin_time < old_timeout->fin_time)
     183                 :          42 :                         break;
     184   [ -  +  #  # ]:           2 :                 if (fin_time == old_timeout->fin_time && id < old_timeout->index)
     185                 :           0 :                         break;
     186      [ -  +  + ]:          44 :         }
     187                 :             : 
     188                 :             :         /*
     189                 :             :          * Mark the timeout active, and insert it into the active list.
     190                 :             :          */
     191                 :        7747 :         all_timeouts[id].indicator = false;
     192                 :        7747 :         all_timeouts[id].start_time = now;
     193                 :        7747 :         all_timeouts[id].fin_time = fin_time;
     194                 :        7747 :         all_timeouts[id].interval_in_ms = interval_in_ms;
     195                 :             : 
     196                 :        7747 :         insert_timeout(id, i);
     197                 :        7747 : }
     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                 :        9039 : schedule_alarm(TimestampTz now)
     211                 :             : {
     212         [ +  + ]:        9039 :         if (num_active_timeouts > 0)
     213                 :             :         {
     214                 :        9038 :                 struct itimerval timeval;
     215                 :        9038 :                 TimestampTz nearest_timeout;
     216                 :        9038 :                 long            secs;
     217                 :        9038 :                 int                     usecs;
     218                 :             : 
     219   [ +  -  +  -  :       45190 :                 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   [ +  +  +  - ]:        9038 :                 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                 :        9038 :                 nearest_timeout = active_timeouts[0]->fin_time;
     240         [ -  + ]:        9038 :                 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                 :           0 :                 }
     247                 :             :                 else
     248                 :             :                 {
     249                 :        9038 :                         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   [ +  +  +  - ]:        9038 :                         if (secs == 0 && usecs == 0)
     257                 :           0 :                                 usecs = 1;
     258                 :             :                 }
     259                 :             : 
     260                 :        9038 :                 timeval.it_value.tv_sec = secs;
     261                 :        9038 :                 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                 :        9038 :                 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   [ +  +  +  + ]:        9038 :                 if (signal_pending && nearest_timeout >= signal_due_at)
     317                 :        8378 :                         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                 :         660 :                 signal_due_at = nearest_timeout;
     336                 :         660 :                 signal_pending = true;
     337                 :             : 
     338                 :             :                 /* Set the alarm timer */
     339         [ +  - ]:         660 :                 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                 :           0 :                 }
     349      [ -  +  + ]:        9038 :         }
     350                 :        9039 : }
     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                 :           5 : 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                 :           5 :         HOLD_INTERRUPTS();
     372                 :             : 
     373                 :             :         /*
     374                 :             :          * SIGALRM is always cause for waking anything waiting on the process
     375                 :             :          * latch.
     376                 :             :          */
     377                 :           5 :         SetLatch(MyLatch);
     378                 :             : 
     379                 :             :         /*
     380                 :             :          * Always reset signal_pending, even if !alarm_enabled, since indeed no
     381                 :             :          * signal is now pending.
     382                 :             :          */
     383                 :           5 :         signal_pending = false;
     384                 :             : 
     385                 :             :         /*
     386                 :             :          * Fire any pending timeouts, but only if we're enabled to do so.
     387                 :             :          */
     388         [ +  - ]:           5 :         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                 :           5 :                 disable_alarm();
     396                 :             : 
     397         [ +  - ]:           5 :                 if (num_active_timeouts > 0)
     398                 :             :                 {
     399                 :           5 :                         TimestampTz now = GetCurrentTimestamp();
     400                 :             : 
     401                 :             :                         /* While the first pending timeout has been reached ... */
     402   [ +  +  +  + ]:          15 :                         while (num_active_timeouts > 0 &&
     403                 :           7 :                                    now >= active_timeouts[0]->fin_time)
     404                 :             :                         {
     405                 :           3 :                                 timeout_params *this_timeout = active_timeouts[0];
     406                 :             : 
     407                 :             :                                 /* Remove it from the active list */
     408                 :           3 :                                 remove_timeout_index(0);
     409                 :             : 
     410                 :             :                                 /* Mark it as fired */
     411                 :           3 :                                 this_timeout->indicator = true;
     412                 :             : 
     413                 :             :                                 /* And call its handler function */
     414                 :           3 :                                 this_timeout->timeout_handler();
     415                 :             : 
     416                 :             :                                 /* If it should fire repeatedly, re-enable it. */
     417         [ +  - ]:           3 :                                 if (this_timeout->interval_in_ms > 0)
     418                 :             :                                 {
     419                 :           0 :                                         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                 :           0 :                                         new_fin_time =
     429                 :           0 :                                                 TimestampTzPlusMilliseconds(this_timeout->fin_time,
     430                 :             :                                                                                                         this_timeout->interval_in_ms);
     431         [ #  # ]:           0 :                                         if (new_fin_time < now)
     432                 :           0 :                                                 new_fin_time =
     433                 :           0 :                                                         TimestampTzPlusMilliseconds(now,
     434                 :             :                                                                                                                 this_timeout->interval_in_ms);
     435                 :           0 :                                         enable_timeout(this_timeout->index, now, new_fin_time,
     436                 :           0 :                                                                    this_timeout->interval_in_ms);
     437                 :           0 :                                 }
     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                 :           3 :                                 now = GetCurrentTimestamp();
     445                 :           3 :                         }
     446                 :             : 
     447                 :             :                         /* Done firing timeouts, so reschedule next interrupt if any */
     448                 :           5 :                         schedule_alarm(now);
     449                 :           5 :                 }
     450                 :           5 :         }
     451                 :             : 
     452         [ +  - ]:           5 :         RESUME_INTERRUPTS();
     453                 :           5 : }
     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                 :        1114 : InitializeTimeouts(void)
     471                 :             : {
     472                 :        1114 :         int                     i;
     473                 :             : 
     474                 :             :         /* Initialize, or re-initialize, all local state */
     475                 :        1114 :         disable_alarm();
     476                 :             : 
     477                 :        1114 :         num_active_timeouts = 0;
     478                 :             : 
     479         [ +  + ]:       26736 :         for (i = 0; i < MAX_TIMEOUTS; i++)
     480                 :             :         {
     481                 :       25622 :                 all_timeouts[i].index = i;
     482                 :       25622 :                 all_timeouts[i].active = false;
     483                 :       25622 :                 all_timeouts[i].indicator = false;
     484                 :       25622 :                 all_timeouts[i].timeout_handler = NULL;
     485                 :       25622 :                 all_timeouts[i].start_time = 0;
     486                 :       25622 :                 all_timeouts[i].fin_time = 0;
     487                 :       25622 :                 all_timeouts[i].interval_in_ms = 0;
     488                 :       25622 :         }
     489                 :             : 
     490                 :        1114 :         all_timeouts_initialized = true;
     491                 :             : 
     492                 :             :         /* Now establish the signal handler */
     493                 :        1114 :         pqsignal(SIGALRM, handle_sig_alarm);
     494                 :        1114 : }
     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                 :        6700 : RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
     506                 :             : {
     507         [ +  - ]:        6700 :         Assert(all_timeouts_initialized);
     508                 :             : 
     509                 :             :         /* There's no need to disable the signal handler here. */
     510                 :             : 
     511         [ +  - ]:        6700 :         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                 :           0 :         }
     522                 :             : 
     523         [ +  - ]:        6700 :         Assert(all_timeouts[id].timeout_handler == NULL);
     524                 :             : 
     525                 :        6700 :         all_timeouts[id].timeout_handler = handler;
     526                 :             : 
     527                 :        6700 :         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                 :        8199 : reschedule_timeouts(void)
     541                 :             : {
     542                 :             :         /* For flexibility, allow this to be called before we're initialized. */
     543         [ +  - ]:        8199 :         if (!all_timeouts_initialized)
     544                 :           0 :                 return;
     545                 :             : 
     546                 :             :         /* Disable timeout interrupts for safety. */
     547                 :        8199 :         disable_alarm();
     548                 :             : 
     549                 :             :         /* Reschedule the interrupt, if any timeouts remain active. */
     550         [ +  + ]:        8199 :         if (num_active_timeouts > 0)
     551                 :        1243 :                 schedule_alarm(GetCurrentTimestamp());
     552                 :        8199 : }
     553                 :             : 
     554                 :             : /*
     555                 :             :  * Enable the specified timeout to fire after the specified delay.
     556                 :             :  *
     557                 :             :  * Delay is given in milliseconds.
     558                 :             :  */
     559                 :             : void
     560                 :        7747 : enable_timeout_after(TimeoutId id, int delay_ms)
     561                 :             : {
     562                 :        7747 :         TimestampTz now;
     563                 :        7747 :         TimestampTz fin_time;
     564                 :             : 
     565                 :             :         /* Disable timeout interrupts for safety. */
     566                 :        7747 :         disable_alarm();
     567                 :             : 
     568                 :             :         /* Queue the timeout at the appropriate time. */
     569                 :        7747 :         now = GetCurrentTimestamp();
     570                 :        7747 :         fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
     571                 :        7747 :         enable_timeout(id, now, fin_time, 0);
     572                 :             : 
     573                 :             :         /* Set the timer interrupt. */
     574                 :        7747 :         schedule_alarm(now);
     575                 :        7747 : }
     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                 :           0 : enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
     585                 :             : {
     586                 :           0 :         TimestampTz now;
     587                 :             : 
     588                 :             :         /* Disable timeout interrupts for safety. */
     589                 :           0 :         disable_alarm();
     590                 :             : 
     591                 :             :         /* Queue the timeout at the appropriate time. */
     592                 :           0 :         now = GetCurrentTimestamp();
     593                 :           0 :         enable_timeout(id, now, fin_time, delay_ms);
     594                 :             : 
     595                 :             :         /* Set the timer interrupt. */
     596                 :           0 :         schedule_alarm(now);
     597                 :           0 : }
     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                 :           0 :         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                 :           0 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
     631                 :             : {
     632                 :           0 :         TimestampTz now;
     633                 :           0 :         int                     i;
     634                 :             : 
     635                 :             :         /* Disable timeout interrupts for safety. */
     636                 :           0 :         disable_alarm();
     637                 :             : 
     638                 :             :         /* Queue the timeout(s) at the appropriate times. */
     639                 :           0 :         now = GetCurrentTimestamp();
     640                 :             : 
     641         [ #  # ]:           0 :         for (i = 0; i < count; i++)
     642                 :             :         {
     643                 :           0 :                 TimeoutId       id = timeouts[i].id;
     644                 :           0 :                 TimestampTz fin_time;
     645                 :             : 
     646   [ #  #  #  # ]:           0 :                 switch (timeouts[i].type)
     647                 :             :                 {
     648                 :             :                         case TMPARAM_AFTER:
     649                 :           0 :                                 fin_time = TimestampTzPlusMilliseconds(now,
     650                 :             :                                                                                                            timeouts[i].delay_ms);
     651                 :           0 :                                 enable_timeout(id, now, fin_time, 0);
     652                 :           0 :                                 break;
     653                 :             : 
     654                 :             :                         case TMPARAM_AT:
     655                 :           0 :                                 enable_timeout(id, now, timeouts[i].fin_time, 0);
     656                 :           0 :                                 break;
     657                 :             : 
     658                 :             :                         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                 :             :                         default:
     665   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unrecognized timeout type %d",
     666                 :             :                                          (int) timeouts[i].type);
     667                 :           0 :                                 break;
     668                 :             :                 }
     669                 :           0 :         }
     670                 :             : 
     671                 :             :         /* Set the timer interrupt. */
     672                 :           0 :         schedule_alarm(now);
     673                 :           0 : }
     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                 :         731 : disable_timeout(TimeoutId id, bool keep_indicator)
     686                 :             : {
     687                 :             :         /* Assert request is sane */
     688         [ +  - ]:         731 :         Assert(all_timeouts_initialized);
     689         [ +  - ]:         731 :         Assert(all_timeouts[id].timeout_handler != NULL);
     690                 :             : 
     691                 :             :         /* Disable timeout interrupts for safety. */
     692                 :         731 :         disable_alarm();
     693                 :             : 
     694                 :             :         /* Find the timeout and remove it from the active list. */
     695         [ +  + ]:         731 :         if (all_timeouts[id].active)
     696                 :         729 :                 remove_timeout_index(find_active_timeout(id));
     697                 :             : 
     698                 :             :         /* Mark it inactive, whether it was active or not. */
     699         [ -  + ]:         731 :         if (!keep_indicator)
     700                 :         731 :                 all_timeouts[id].indicator = false;
     701                 :             : 
     702                 :             :         /* Reschedule the interrupt, if any timeouts remain active. */
     703         [ +  + ]:         731 :         if (num_active_timeouts > 0)
     704                 :          44 :                 schedule_alarm(GetCurrentTimestamp());
     705                 :         731 : }
     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                 :           1 : disable_timeouts(const DisableTimeoutParams *timeouts, int count)
     719                 :             : {
     720                 :           1 :         int                     i;
     721                 :             : 
     722         [ +  - ]:           1 :         Assert(all_timeouts_initialized);
     723                 :             : 
     724                 :             :         /* Disable timeout interrupts for safety. */
     725                 :           1 :         disable_alarm();
     726                 :             : 
     727                 :             :         /* Cancel the timeout(s). */
     728         [ +  + ]:           3 :         for (i = 0; i < count; i++)
     729                 :             :         {
     730                 :           2 :                 TimeoutId       id = timeouts[i].id;
     731                 :             : 
     732         [ +  - ]:           2 :                 Assert(all_timeouts[id].timeout_handler != NULL);
     733                 :             : 
     734         [ +  + ]:           2 :                 if (all_timeouts[id].active)
     735                 :           1 :                         remove_timeout_index(find_active_timeout(id));
     736                 :             : 
     737         [ +  + ]:           2 :                 if (!timeouts[i].keep_indicator)
     738                 :           1 :                         all_timeouts[id].indicator = false;
     739                 :           2 :         }
     740                 :             : 
     741                 :             :         /* Reschedule the interrupt, if any timeouts remain active. */
     742         [ +  - ]:           1 :         if (num_active_timeouts > 0)
     743                 :           0 :                 schedule_alarm(GetCurrentTimestamp());
     744                 :           1 : }
     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                 :        6763 : disable_all_timeouts(bool keep_indicators)
     752                 :             : {
     753                 :        6763 :         int                     i;
     754                 :             : 
     755                 :        6763 :         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                 :        6763 :         num_active_timeouts = 0;
     764                 :             : 
     765         [ +  + ]:      162312 :         for (i = 0; i < MAX_TIMEOUTS; i++)
     766                 :             :         {
     767                 :      155549 :                 all_timeouts[i].active = false;
     768         [ -  + ]:      155549 :                 if (!keep_indicators)
     769                 :      155549 :                         all_timeouts[i].indicator = false;
     770                 :      155549 :         }
     771                 :        6763 : }
     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                 :      276701 : get_timeout_active(TimeoutId id)
     781                 :             : {
     782                 :      276701 :         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                 :           0 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
     794                 :             : {
     795         [ #  # ]:           0 :         if (all_timeouts[id].indicator)
     796                 :             :         {
     797         [ #  # ]:           0 :                 if (reset_indicator)
     798                 :           0 :                         all_timeouts[id].indicator = false;
     799                 :           0 :                 return true;
     800                 :             :         }
     801                 :           0 :         return false;
     802                 :           0 : }
     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                 :          47 : get_timeout_start_time(TimeoutId id)
     814                 :             : {
     815                 :          47 :         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.3.2-1