LCOV - differential code coverage report
Current view: top level - src/backend/utils/misc - timeout.c (source / functions) Coverage Total Hit UBC GBC GNC CBC DCB
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 83.3 % 204 170 34 3 167 2
Current Date: 2024-04-14 14:21:10 Functions: 90.0 % 20 18 2 1 17
Baseline: 16@8cea358b128 Branches: 62.1 % 140 87 53 1 86
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 83.3 % 204 170 34 3 167
Function coverage date bins:
(240..) days: 90.0 % 20 18 2 1 17
Branch coverage date bins:
(240..) days: 62.1 % 140 87 53 1 86

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * timeout.c
                                  4                 :                :  *    Routines to multiplex SIGALRM interrupts for multiple timeout reasons.
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
                                  7                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :                :  *
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *    src/backend/utils/misc/timeout.c
                                 12                 :                :  *
                                 13                 :                :  *-------------------------------------------------------------------------
                                 14                 :                :  */
                                 15                 :                : #include "postgres.h"
                                 16                 :                : 
                                 17                 :                : #include <sys/time.h>
                                 18                 :                : 
                                 19                 :                : #include "miscadmin.h"
                                 20                 :                : #include "storage/latch.h"
                                 21                 :                : #include "utils/timeout.h"
                                 22                 :                : #include "utils/timestamp.h"
                                 23                 :                : 
                                 24                 :                : 
                                 25                 :                : /* Data about any one timeout reason */
                                 26                 :                : typedef struct timeout_params
                                 27                 :                : {
                                 28                 :                :     TimeoutId   index;          /* identifier of timeout reason */
                                 29                 :                : 
                                 30                 :                :     /* volatile because these may be changed from the signal handler */
                                 31                 :                :     volatile bool active;       /* true if timeout is in active_timeouts[] */
                                 32                 :                :     volatile bool indicator;    /* true if timeout has occurred */
                                 33                 :                : 
                                 34                 :                :     /* callback function for timeout, or NULL if timeout not registered */
                                 35                 :                :     timeout_handler_proc timeout_handler;
                                 36                 :                : 
                                 37                 :                :     TimestampTz start_time;     /* time that timeout was last activated */
                                 38                 :                :     TimestampTz fin_time;       /* time it is, or was last, due to fire */
                                 39                 :                :     int         interval_in_ms; /* time between firings, or 0 if just once */
                                 40                 :                : } timeout_params;
                                 41                 :                : 
                                 42                 :                : /*
                                 43                 :                :  * List of possible timeout reasons in the order of enum TimeoutId.
                                 44                 :                :  */
                                 45                 :                : static timeout_params all_timeouts[MAX_TIMEOUTS];
                                 46                 :                : static bool all_timeouts_initialized = false;
                                 47                 :                : 
                                 48                 :                : /*
                                 49                 :                :  * List of active timeouts ordered by their fin_time and priority.
                                 50                 :                :  * This list is subject to change by the interrupt handler, so it's volatile.
                                 51                 :                :  */
                                 52                 :                : static volatile int num_active_timeouts = 0;
                                 53                 :                : static timeout_params *volatile active_timeouts[MAX_TIMEOUTS];
                                 54                 :                : 
                                 55                 :                : /*
                                 56                 :                :  * Flag controlling whether the signal handler is allowed to do anything.
                                 57                 :                :  * This is useful to avoid race conditions with the handler.  Note in
                                 58                 :                :  * particular that this lets us make changes in the data structures without
                                 59                 :                :  * tediously disabling and re-enabling the timer signal.  Most of the time,
                                 60                 :                :  * no interrupt would happen anyway during such critical sections, but if
                                 61                 :                :  * one does, this rule ensures it's safe.  Leaving the signal enabled across
                                 62                 :                :  * multiple operations can greatly reduce the number of kernel calls we make,
                                 63                 :                :  * too.  See comments in schedule_alarm() about that.
                                 64                 :                :  *
                                 65                 :                :  * We leave this "false" when we're not expecting interrupts, just in case.
                                 66                 :                :  */
                                 67                 :                : static volatile sig_atomic_t alarm_enabled = false;
                                 68                 :                : 
                                 69                 :                : #define disable_alarm() (alarm_enabled = false)
                                 70                 :                : #define enable_alarm()  (alarm_enabled = true)
                                 71                 :                : 
                                 72                 :                : /*
                                 73                 :                :  * State recording if and when we next expect the interrupt to fire.
                                 74                 :                :  * (signal_due_at is valid only when signal_pending is true.)
                                 75                 :                :  * Note that the signal handler will unconditionally reset signal_pending to
                                 76                 :                :  * false, so that can change asynchronously even when alarm_enabled is false.
                                 77                 :                :  */
                                 78                 :                : static volatile sig_atomic_t signal_pending = false;
                                 79                 :                : static volatile TimestampTz signal_due_at = 0;
                                 80                 :                : 
                                 81                 :                : 
                                 82                 :                : /*****************************************************************************
                                 83                 :                :  * Internal helper functions
                                 84                 :                :  *
                                 85                 :                :  * For all of these, it is caller's responsibility to protect them from
                                 86                 :                :  * interruption by the signal handler.  Generally, call disable_alarm()
                                 87                 :                :  * first to prevent interruption, then update state, and last call
                                 88                 :                :  * schedule_alarm(), which will re-enable the signal handler if needed.
                                 89                 :                :  *****************************************************************************/
                                 90                 :                : 
                                 91                 :                : /*
                                 92                 :                :  * Find the index of a given timeout reason in the active array.
                                 93                 :                :  * If it's not there, return -1.
                                 94                 :                :  */
                                 95                 :                : static int
 4290 alvherre@alvh.no-ip.       96                 :CBC       25531 : find_active_timeout(TimeoutId id)
                                 97                 :                : {
                                 98                 :                :     int         i;
                                 99                 :                : 
                                100         [ +  - ]:          25660 :     for (i = 0; i < num_active_timeouts; i++)
                                101                 :                :     {
                                102         [ +  + ]:          25660 :         if (active_timeouts[i]->index == id)
                                103                 :          25531 :             return i;
                                104                 :                :     }
                                105                 :                : 
 4290 alvherre@alvh.no-ip.      106                 :UBC           0 :     return -1;
                                107                 :                : }
                                108                 :                : 
                                109                 :                : /*
                                110                 :                :  * Insert specified timeout reason into the list of active timeouts
                                111                 :                :  * at the given index.
                                112                 :                :  */
                                113                 :                : static void
 4290 alvherre@alvh.no-ip.      114                 :CBC       56142 : insert_timeout(TimeoutId id, int index)
                                115                 :                : {
                                116                 :                :     int         i;
                                117                 :                : 
                                118   [ +  -  -  + ]:          56142 :     if (index < 0 || index > num_active_timeouts)
 4290 alvherre@alvh.no-ip.      119         [ #  # ]:UBC           0 :         elog(FATAL, "timeout index %d out of range 0..%d", index,
                                120                 :                :              num_active_timeouts);
                                121                 :                : 
 1633 tgl@sss.pgh.pa.us         122         [ -  + ]:CBC       56142 :     Assert(!all_timeouts[id].active);
                                123                 :          56142 :     all_timeouts[id].active = true;
                                124                 :                : 
 4290 alvherre@alvh.no-ip.      125         [ +  + ]:          57093 :     for (i = num_active_timeouts - 1; i >= index; i--)
                                126                 :            951 :         active_timeouts[i + 1] = active_timeouts[i];
                                127                 :                : 
                                128                 :          56142 :     active_timeouts[index] = &all_timeouts[id];
                                129                 :                : 
                                130                 :          56142 :     num_active_timeouts++;
                                131                 :          56142 : }
                                132                 :                : 
                                133                 :                : /*
                                134                 :                :  * Remove the index'th element from the timeout list.
                                135                 :                :  */
                                136                 :                : static void
                                137                 :          25635 : remove_timeout_index(int index)
                                138                 :                : {
                                139                 :                :     int         i;
                                140                 :                : 
                                141   [ +  -  -  + ]:          25635 :     if (index < 0 || index >= num_active_timeouts)
 4290 alvherre@alvh.no-ip.      142         [ #  # ]:UBC           0 :         elog(FATAL, "timeout index %d out of range 0..%d", index,
                                143                 :                :              num_active_timeouts - 1);
                                144                 :                : 
 1633 tgl@sss.pgh.pa.us         145         [ -  + ]:CBC       25635 :     Assert(active_timeouts[index]->active);
                                146                 :          25635 :     active_timeouts[index]->active = false;
                                147                 :                : 
 4290 alvherre@alvh.no-ip.      148         [ +  + ]:          26697 :     for (i = index + 1; i < num_active_timeouts; i++)
                                149                 :           1062 :         active_timeouts[i - 1] = active_timeouts[i];
                                150                 :                : 
                                151                 :          25635 :     num_active_timeouts--;
                                152                 :          25635 : }
                                153                 :                : 
                                154                 :                : /*
                                155                 :                :  * Enable the specified timeout reason
                                156                 :                :  */
                                157                 :                : static void
  927 rhaas@postgresql.org      158                 :          56142 : enable_timeout(TimeoutId id, TimestampTz now, TimestampTz fin_time,
                                159                 :                :                int interval_in_ms)
                                160                 :                : {
                                161                 :                :     int         i;
                                162                 :                : 
                                163                 :                :     /* Assert request is sane */
 4047 tgl@sss.pgh.pa.us         164         [ -  + ]:          56142 :     Assert(all_timeouts_initialized);
                                165         [ -  + ]:          56142 :     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                 :                :      */
 1633                           171         [ -  + ]:          56142 :     if (all_timeouts[id].active)
 1633 tgl@sss.pgh.pa.us         172                 :UBC           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                 :                :      */
 4047 tgl@sss.pgh.pa.us         178         [ +  + ]:CBC       56392 :     for (i = 0; i < num_active_timeouts; i++)
                                179                 :                :     {
                                180                 :           1191 :         timeout_params *old_timeout = active_timeouts[i];
                                181                 :                : 
                                182         [ +  + ]:           1191 :         if (fin_time < old_timeout->fin_time)
                                183                 :            941 :             break;
                                184   [ -  +  -  - ]:            250 :         if (fin_time == old_timeout->fin_time && id < old_timeout->index)
 4047 tgl@sss.pgh.pa.us         185                 :UBC           0 :             break;
                                186                 :                :     }
                                187                 :                : 
                                188                 :                :     /*
                                189                 :                :      * Mark the timeout active, and insert it into the active list.
                                190                 :                :      */
 4047 tgl@sss.pgh.pa.us         191                 :CBC       56142 :     all_timeouts[id].indicator = false;
                                192                 :          56142 :     all_timeouts[id].start_time = now;
                                193                 :          56142 :     all_timeouts[id].fin_time = fin_time;
  927 rhaas@postgresql.org      194                 :          56142 :     all_timeouts[id].interval_in_ms = interval_in_ms;
                                195                 :                : 
 4047 tgl@sss.pgh.pa.us         196                 :          56142 :     insert_timeout(id, i);
                                197                 :          56142 : }
                                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
 4290 alvherre@alvh.no-ip.      210                 :          63748 : schedule_alarm(TimestampTz now)
                                211                 :                : {
                                212         [ +  + ]:          63748 :     if (num_active_timeouts > 0)
                                213                 :                :     {
                                214                 :                :         struct itimerval timeval;
                                215                 :                :         TimestampTz nearest_timeout;
                                216                 :                :         long        secs;
                                217                 :                :         int         usecs;
                                218                 :                : 
                                219   [ +  -  +  -  :         318545 :         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                 :                :          */
  794 tgl@sss.pgh.pa.us         230   [ +  +  -  + ]:          63709 :         if (signal_pending && now > signal_due_at + 10 * 1000)
  794 tgl@sss.pgh.pa.us         231                 :UBC           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                 :                :          */
 1194 tgl@sss.pgh.pa.us         239                 :CBC       63709 :         nearest_timeout = active_timeouts[0]->fin_time;
                                240         [ -  + ]:          63709 :         if (now > nearest_timeout)
                                241                 :                :         {
 1194 tgl@sss.pgh.pa.us         242                 :UBC           0 :             signal_pending = false;
                                243                 :                :             /* force an interrupt as soon as possible */
                                244                 :              0 :             secs = 0;
 4290 alvherre@alvh.no-ip.      245                 :              0 :             usecs = 1;
                                246                 :                :         }
                                247                 :                :         else
                                248                 :                :         {
 1194 tgl@sss.pgh.pa.us         249                 :CBC       63709 :             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   [ +  +  -  + ]:          63709 :             if (secs == 0 && usecs == 0)
 1194 tgl@sss.pgh.pa.us         257                 :UBC           0 :                 usecs = 1;
                                258                 :                :         }
                                259                 :                : 
 4290 alvherre@alvh.no-ip.      260                 :CBC       63709 :         timeval.it_value.tv_sec = secs;
                                261                 :          63709 :         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                 :                :          */
 4046 tgl@sss.pgh.pa.us         293                 :          63709 :         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                 :                :          */
 1194                           316   [ +  +  +  + ]:          63709 :         if (signal_pending && nearest_timeout >= signal_due_at)
                                317                 :          41003 :             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                 :          22706 :         signal_due_at = nearest_timeout;
                                336                 :          22706 :         signal_pending = true;
                                337                 :                : 
                                338                 :                :         /* Set the alarm timer */
 4290 alvherre@alvh.no-ip.      339         [ -  + ]:          22706 :         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                 :                :              */
 1194 tgl@sss.pgh.pa.us         346                 :UBC           0 :             signal_pending = false;
 4290 alvherre@alvh.no-ip.      347         [ #  # ]:              0 :             elog(FATAL, "could not enable SIGALRM timer: %m");
                                348                 :                :         }
                                349                 :                :     }
                                350                 :                : }
                                351                 :                : 
                                352                 :                : 
                                353                 :                : /*****************************************************************************
                                354                 :                :  * Signal handler
                                355                 :                :  *****************************************************************************/
                                356                 :                : 
                                357                 :                : /*
                                358                 :                :  * Signal handler for SIGALRM
                                359                 :                :  *
                                360                 :                :  * Process any active timeout reasons and then reschedule the interrupt
                                361                 :                :  * as needed.
                                362                 :                :  */
                                363                 :                : static void
 4290 alvherre@alvh.no-ip.      364                 :CBC         273 : 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                 :                :      */
 3789 tgl@sss.pgh.pa.us         371                 :            273 :     HOLD_INTERRUPTS();
                                372                 :                : 
                                373                 :                :     /*
                                374                 :                :      * SIGALRM is always cause for waking anything waiting on the process
                                375                 :                :      * latch.
                                376                 :                :      */
 3378 andres@anarazel.de        377                 :            273 :     SetLatch(MyLatch);
                                378                 :                : 
                                379                 :                :     /*
                                380                 :                :      * Always reset signal_pending, even if !alarm_enabled, since indeed no
                                381                 :                :      * signal is now pending.
                                382                 :                :      */
 1194 tgl@sss.pgh.pa.us         383                 :            273 :     signal_pending = false;
                                384                 :                : 
                                385                 :                :     /*
                                386                 :                :      * Fire any pending timeouts, but only if we're enabled to do so.
                                387                 :                :      */
 4046                           388         [ +  + ]:            273 :     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                 :            220 :         disable_alarm();
                                396                 :                : 
                                397         [ +  - ]:            220 :         if (num_active_timeouts > 0)
                                398                 :                :         {
                                399                 :            220 :             TimestampTz now = GetCurrentTimestamp();
                                400                 :                : 
                                401                 :                :             /* While the first pending timeout has been reached ... */
                                402         [ +  + ]:            324 :             while (num_active_timeouts > 0 &&
                                403         [ +  + ]:            285 :                    now >= active_timeouts[0]->fin_time)
                                404                 :                :             {
                                405                 :            104 :                 timeout_params *this_timeout = active_timeouts[0];
                                406                 :                : 
                                407                 :                :                 /* Remove it from the active list */
                                408                 :            104 :                 remove_timeout_index(0);
                                409                 :                : 
                                410                 :                :                 /* Mark it as fired */
                                411                 :            104 :                 this_timeout->indicator = true;
                                412                 :                : 
                                413                 :                :                 /* And call its handler function */
 2411 peter_e@gmx.net           414                 :            104 :                 this_timeout->timeout_handler();
                                415                 :                : 
                                416                 :                :                 /* If it should fire repeatedly, re-enable it. */
  927 rhaas@postgresql.org      417         [ +  + ]:            104 :                 if (this_timeout->interval_in_ms > 0)
                                418                 :                :                 {
                                419                 :                :                     TimestampTz new_fin_time;
                                420                 :                : 
                                421                 :                :                     /*
                                422                 :                :                      * To guard against drift, schedule the next instance of
                                423                 :                :                      * the timeout based on the intended firing time rather
                                424                 :                :                      * than the actual firing time. But if the timeout was so
                                425                 :                :                      * late that we missed an entire cycle, fall back to
                                426                 :                :                      * scheduling based on the actual firing time.
                                427                 :                :                      */
                                428                 :             26 :                     new_fin_time =
                                429                 :             26 :                         TimestampTzPlusMilliseconds(this_timeout->fin_time,
                                430                 :                :                                                     this_timeout->interval_in_ms);
                                431         [ -  + ]:             26 :                     if (new_fin_time < now)
  927 rhaas@postgresql.org      432                 :UBC           0 :                         new_fin_time =
                                433                 :              0 :                             TimestampTzPlusMilliseconds(now,
                                434                 :                :                                                         this_timeout->interval_in_ms);
  927 rhaas@postgresql.org      435                 :CBC          26 :                     enable_timeout(this_timeout->index, now, new_fin_time,
                                436                 :                :                                    this_timeout->interval_in_ms);
                                437                 :                :                 }
                                438                 :                : 
                                439                 :                :                 /*
                                440                 :                :                  * The handler might not take negligible time (CheckDeadLock
                                441                 :                :                  * for instance isn't too cheap), so let's update our idea of
                                442                 :                :                  * "now" after each one.
                                443                 :                :                  */
 4046 tgl@sss.pgh.pa.us         444                 :            104 :                 now = GetCurrentTimestamp();
                                445                 :                :             }
                                446                 :                : 
                                447                 :                :             /* Done firing timeouts, so reschedule next interrupt if any */
                                448                 :            220 :             schedule_alarm(now);
                                449                 :                :         }
                                450                 :                :     }
                                451                 :                : 
 3789                           452         [ -  + ]:            273 :     RESUME_INTERRUPTS();
 4290 alvherre@alvh.no-ip.      453                 :            273 : }
                                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                 :          28723 : InitializeTimeouts(void)
                                471                 :                : {
                                472                 :                :     int         i;
                                473                 :                : 
                                474                 :                :     /* Initialize, or re-initialize, all local state */
 4046 tgl@sss.pgh.pa.us         475                 :          28723 :     disable_alarm();
                                476                 :                : 
 4290 alvherre@alvh.no-ip.      477                 :          28723 :     num_active_timeouts = 0;
                                478                 :                : 
                                479         [ +  + ]:         689352 :     for (i = 0; i < MAX_TIMEOUTS; i++)
                                480                 :                :     {
                                481                 :         660629 :         all_timeouts[i].index = i;
 1633 tgl@sss.pgh.pa.us         482                 :         660629 :         all_timeouts[i].active = false;
 4290 alvherre@alvh.no-ip.      483                 :         660629 :         all_timeouts[i].indicator = false;
                                484                 :         660629 :         all_timeouts[i].timeout_handler = NULL;
                                485                 :         660629 :         all_timeouts[i].start_time = 0;
                                486                 :         660629 :         all_timeouts[i].fin_time = 0;
  927 rhaas@postgresql.org      487                 :         660629 :         all_timeouts[i].interval_in_ms = 0;
                                488                 :                :     }
                                489                 :                : 
 4290 alvherre@alvh.no-ip.      490                 :          28723 :     all_timeouts_initialized = true;
                                491                 :                : 
                                492                 :                :     /* Now establish the signal handler */
                                493                 :          28723 :     pqsignal(SIGALRM, handle_sig_alarm);
                                494                 :          28723 : }
                                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
 4047 tgl@sss.pgh.pa.us         505                 :         145930 : RegisterTimeout(TimeoutId id, timeout_handler_proc handler)
                                506                 :                : {
 4290 alvherre@alvh.no-ip.      507         [ -  + ]:         145930 :     Assert(all_timeouts_initialized);
                                508                 :                : 
                                509                 :                :     /* There's no need to disable the signal handler here. */
                                510                 :                : 
                                511         [ -  + ]:         145930 :     if (id >= USER_TIMEOUT)
                                512                 :                :     {
                                513                 :                :         /* Allocate a user-defined timeout reason */
 4290 alvherre@alvh.no-ip.      514         [ #  # ]:UBC           0 :         for (id = USER_TIMEOUT; id < MAX_TIMEOUTS; id++)
                                515         [ #  # ]:              0 :             if (all_timeouts[id].timeout_handler == NULL)
                                516                 :              0 :                 break;
                                517         [ #  # ]:              0 :         if (id >= MAX_TIMEOUTS)
                                518         [ #  # ]:              0 :             ereport(FATAL,
                                519                 :                :                     (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
                                520                 :                :                      errmsg("cannot add more timeout reasons")));
                                521                 :                :     }
                                522                 :                : 
 4290 alvherre@alvh.no-ip.      523         [ -  + ]:CBC      145930 :     Assert(all_timeouts[id].timeout_handler == NULL);
                                524                 :                : 
                                525                 :         145930 :     all_timeouts[id].timeout_handler = handler;
                                526                 :                : 
                                527                 :         145930 :     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
 3789 tgl@sss.pgh.pa.us         540                 :          27410 : reschedule_timeouts(void)
                                541                 :                : {
                                542                 :                :     /* For flexibility, allow this to be called before we're initialized. */
                                543         [ -  + ]:          27410 :     if (!all_timeouts_initialized)
 3789 tgl@sss.pgh.pa.us         544                 :UBC           0 :         return;
                                545                 :                : 
                                546                 :                :     /* Disable timeout interrupts for safety. */
 3789 tgl@sss.pgh.pa.us         547                 :CBC       27410 :     disable_alarm();
                                548                 :                : 
                                549                 :                :     /* Reschedule the interrupt, if any timeouts remain active. */
                                550         [ +  + ]:          27410 :     if (num_active_timeouts > 0)
                                551                 :           6604 :         schedule_alarm(GetCurrentTimestamp());
                                552                 :                : }
                                553                 :                : 
                                554                 :                : /*
                                555                 :                :  * Enable the specified timeout to fire after the specified delay.
                                556                 :                :  *
                                557                 :                :  * Delay is given in milliseconds.
                                558                 :                :  */
                                559                 :                : void
 4290 alvherre@alvh.no-ip.      560                 :          55365 : enable_timeout_after(TimeoutId id, int delay_ms)
                                561                 :                : {
                                562                 :                :     TimestampTz now;
                                563                 :                :     TimestampTz fin_time;
                                564                 :                : 
                                565                 :                :     /* Disable timeout interrupts for safety. */
 4046 tgl@sss.pgh.pa.us         566                 :          55365 :     disable_alarm();
                                567                 :                : 
                                568                 :                :     /* Queue the timeout at the appropriate time. */
 4290 alvherre@alvh.no-ip.      569                 :          55365 :     now = GetCurrentTimestamp();
                                570                 :          55365 :     fin_time = TimestampTzPlusMilliseconds(now, delay_ms);
  927 rhaas@postgresql.org      571                 :          55365 :     enable_timeout(id, now, fin_time, 0);
                                572                 :                : 
                                573                 :                :     /* Set the timer interrupt. */
                                574                 :          55365 :     schedule_alarm(now);
                                575                 :          55365 : }
                                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                 :            495 : enable_timeout_every(TimeoutId id, TimestampTz fin_time, int delay_ms)
                                585                 :                : {
                                586                 :                :     TimestampTz now;
                                587                 :                : 
                                588                 :                :     /* Disable timeout interrupts for safety. */
                                589                 :            495 :     disable_alarm();
                                590                 :                : 
                                591                 :                :     /* Queue the timeout at the appropriate time. */
                                592                 :            495 :     now = GetCurrentTimestamp();
                                593                 :            495 :     enable_timeout(id, now, fin_time, delay_ms);
                                594                 :                : 
                                595                 :                :     /* Set the timer interrupt. */
 4047 tgl@sss.pgh.pa.us         596                 :            495 :     schedule_alarm(now);
 4290 alvherre@alvh.no-ip.      597                 :            495 : }
                                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
 4290 alvherre@alvh.no-ip.      607                 :UBC           0 : enable_timeout_at(TimeoutId id, TimestampTz fin_time)
                                608                 :                : {
                                609                 :                :     TimestampTz now;
                                610                 :                : 
                                611                 :                :     /* Disable timeout interrupts for safety. */
 4046 tgl@sss.pgh.pa.us         612                 :              0 :     disable_alarm();
                                613                 :                : 
                                614                 :                :     /* Queue the timeout at the appropriate time. */
 4047                           615                 :              0 :     now = GetCurrentTimestamp();
  927 rhaas@postgresql.org      616                 :              0 :     enable_timeout(id, now, fin_time, 0);
                                617                 :                : 
                                618                 :                :     /* Set the timer interrupt. */
 4047 tgl@sss.pgh.pa.us         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
 4047 tgl@sss.pgh.pa.us         630                 :CBC         128 : enable_timeouts(const EnableTimeoutParams *timeouts, int count)
                                631                 :                : {
                                632                 :                :     TimestampTz now;
                                633                 :                :     int         i;
                                634                 :                : 
                                635                 :                :     /* Disable timeout interrupts for safety. */
 4046                           636                 :            128 :     disable_alarm();
                                637                 :                : 
                                638                 :                :     /* Queue the timeout(s) at the appropriate times. */
 4047                           639                 :            128 :     now = GetCurrentTimestamp();
                                640                 :                : 
                                641         [ +  + ]:            384 :     for (i = 0; i < count; i++)
                                642                 :                :     {
                                643                 :            256 :         TimeoutId   id = timeouts[i].id;
                                644                 :                :         TimestampTz fin_time;
                                645                 :                : 
                                646   [ +  +  -  - ]:            256 :         switch (timeouts[i].type)
                                647                 :                :         {
                                648                 :            243 :             case TMPARAM_AFTER:
                                649                 :            243 :                 fin_time = TimestampTzPlusMilliseconds(now,
                                650                 :                :                                                        timeouts[i].delay_ms);
  927 rhaas@postgresql.org      651                 :            243 :                 enable_timeout(id, now, fin_time, 0);
 4047 tgl@sss.pgh.pa.us         652                 :            243 :                 break;
                                653                 :                : 
 4047 tgl@sss.pgh.pa.us         654                 :GBC          13 :             case TMPARAM_AT:
  927 rhaas@postgresql.org      655                 :             13 :                 enable_timeout(id, now, timeouts[i].fin_time, 0);
                                656                 :             13 :                 break;
                                657                 :                : 
  927 rhaas@postgresql.org      658                 :UBC           0 :             case TMPARAM_EVERY:
                                659                 :              0 :                 fin_time = TimestampTzPlusMilliseconds(now,
                                660                 :                :                                                        timeouts[i].delay_ms);
                                661                 :              0 :                 enable_timeout(id, now, fin_time, timeouts[i].delay_ms);
 4047 tgl@sss.pgh.pa.us         662                 :              0 :                 break;
                                663                 :                : 
                                664                 :              0 :             default:
                                665         [ #  # ]:              0 :                 elog(ERROR, "unrecognized timeout type %d",
                                666                 :                :                      (int) timeouts[i].type);
                                667                 :                :                 break;
                                668                 :                :         }
                                669                 :                :     }
                                670                 :                : 
                                671                 :                :     /* Set the timer interrupt. */
 4047 tgl@sss.pgh.pa.us         672                 :CBC         128 :     schedule_alarm(now);
 4290 alvherre@alvh.no-ip.      673                 :            128 : }
                                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                 :          25679 : disable_timeout(TimeoutId id, bool keep_indicator)
                                686                 :                : {
                                687                 :                :     /* Assert request is sane */
                                688         [ -  + ]:          25679 :     Assert(all_timeouts_initialized);
                                689         [ -  + ]:          25679 :     Assert(all_timeouts[id].timeout_handler != NULL);
                                690                 :                : 
                                691                 :                :     /* Disable timeout interrupts for safety. */
 4046 tgl@sss.pgh.pa.us         692                 :          25679 :     disable_alarm();
                                693                 :                : 
                                694                 :                :     /* Find the timeout and remove it from the active list. */
 1633                           695         [ +  + ]:          25679 :     if (all_timeouts[id].active)
                                696                 :          25274 :         remove_timeout_index(find_active_timeout(id));
                                697                 :                : 
                                698                 :                :     /* Mark it inactive, whether it was active or not. */
 4290 alvherre@alvh.no-ip.      699         [ +  - ]:          25679 :     if (!keep_indicator)
                                700                 :          25679 :         all_timeouts[id].indicator = false;
                                701                 :                : 
                                702                 :                :     /* Reschedule the interrupt, if any timeouts remain active. */
 4047 tgl@sss.pgh.pa.us         703         [ +  + ]:          25679 :     if (num_active_timeouts > 0)
                                704                 :            797 :         schedule_alarm(GetCurrentTimestamp());
                                705                 :          25679 : }
                                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                 :            148 : disable_timeouts(const DisableTimeoutParams *timeouts, int count)
                                719                 :                : {
                                720                 :                :     int         i;
                                721                 :                : 
                                722         [ -  + ]:            148 :     Assert(all_timeouts_initialized);
                                723                 :                : 
                                724                 :                :     /* Disable timeout interrupts for safety. */
 4046                           725                 :            148 :     disable_alarm();
                                726                 :                : 
                                727                 :                :     /* Cancel the timeout(s). */
 4047                           728         [ +  + ]:            444 :     for (i = 0; i < count; i++)
                                729                 :                :     {
                                730                 :            296 :         TimeoutId   id = timeouts[i].id;
                                731                 :                : 
                                732         [ -  + ]:            296 :         Assert(all_timeouts[id].timeout_handler != NULL);
                                733                 :                : 
 1633                           734         [ +  + ]:            296 :         if (all_timeouts[id].active)
                                735                 :            257 :             remove_timeout_index(find_active_timeout(id));
                                736                 :                : 
 4047                           737         [ +  + ]:            296 :         if (!timeouts[i].keep_indicator)
                                738                 :            148 :             all_timeouts[id].indicator = false;
                                739                 :                :     }
                                740                 :                : 
                                741                 :                :     /* Reschedule the interrupt, if any timeouts remain active. */
 4290 alvherre@alvh.no-ip.      742         [ +  + ]:            148 :     if (num_active_timeouts > 0)
                                743                 :            139 :         schedule_alarm(GetCurrentTimestamp());
                                744                 :            148 : }
                                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                 :          19993 : disable_all_timeouts(bool keep_indicators)
                                752                 :                : {
                                753                 :                :     int         i;
                                754                 :                : 
 4046 tgl@sss.pgh.pa.us         755                 :          19993 :     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                 :                : 
 4290 alvherre@alvh.no-ip.      763                 :          19993 :     num_active_timeouts = 0;
                                764                 :                : 
 1633 tgl@sss.pgh.pa.us         765         [ +  + ]:         479832 :     for (i = 0; i < MAX_TIMEOUTS; i++)
                                766                 :                :     {
                                767                 :         459839 :         all_timeouts[i].active = false;
                                768         [ +  - ]:         459839 :         if (!keep_indicators)
 4290 alvherre@alvh.no-ip.      769                 :         459839 :             all_timeouts[i].indicator = false;
                                770                 :                :     }
                                771                 :          19993 : }
                                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
 1633 tgl@sss.pgh.pa.us         780                 :        1448526 : get_timeout_active(TimeoutId id)
                                781                 :                : {
                                782                 :        1448526 :     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
 4047                           793                 :            108 : get_timeout_indicator(TimeoutId id, bool reset_indicator)
                                794                 :                : {
                                795         [ +  + ]:            108 :     if (all_timeouts[id].indicator)
                                796                 :                :     {
                                797         [ +  - ]:             10 :         if (reset_indicator)
                                798                 :             10 :             all_timeouts[id].indicator = false;
                                799                 :             10 :         return true;
                                800                 :                :     }
                                801                 :             98 :     return false;
                                802                 :                : }
                                803                 :                : 
                                804                 :                : /*
                                805                 :                :  * Return the time when the timeout was most recently activated
                                806                 :                :  *
                                807                 :                :  * Note: will return 0 if timeout has never been activated in this process.
                                808                 :                :  * However, we do *not* reset the start_time when a timeout occurs, so as
                                809                 :                :  * not to create a race condition if SIGALRM fires just as some code is
                                810                 :                :  * about to fetch the value.
                                811                 :                :  */
                                812                 :                : TimestampTz
 4290 alvherre@alvh.no-ip.      813                 :           1121 : get_timeout_start_time(TimeoutId id)
                                814                 :                : {
                                815                 :           1121 :     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
 2879 tgl@sss.pgh.pa.us         827                 :UBC           0 : get_timeout_finish_time(TimeoutId id)
                                828                 :                : {
                                829                 :              0 :     return all_timeouts[id].fin_time;
                                830                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622