LCOV - differential code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Coverage Total Hit UBC CBC
Current: 806555e3000d0b0e0c536c1dc65548128d457d86 vs 1d325ad99cb2dec0e8b45ba36909ee0a497d2a57 Lines: 100.0 % 94 94 94
Current Date: 2025-12-17 08:58:58 +0900 Functions: 100.0 % 7 7 7
Baseline: lcov-20251217-005640-baseline Branches: 93.3 % 60 56 4 56
Baseline Date: 2025-12-16 12:57:12 -0800 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(360..) days: 100.0 % 94 94 94
Function coverage date bins:
(360..) days: 100.0 % 7 7 7
Branch coverage date bins:
(360..) days: 93.3 % 60 56 4 56

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * condition_variable.c
                                  4                 :                :  *    Implementation of condition variables.  Condition variables provide
                                  5                 :                :  *    a way for one process to wait until a specific condition occurs,
                                  6                 :                :  *    without needing to know the specific identity of the process for
                                  7                 :                :  *    which they are waiting.  Waits for condition variables can be
                                  8                 :                :  *    interrupted, unlike LWLock waits.  Condition variables are safe
                                  9                 :                :  *    to use within dynamic shared memory segments.
                                 10                 :                :  *
                                 11                 :                :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
                                 12                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                 13                 :                :  *
                                 14                 :                :  * src/backend/storage/lmgr/condition_variable.c
                                 15                 :                :  *
                                 16                 :                :  *-------------------------------------------------------------------------
                                 17                 :                :  */
                                 18                 :                : 
                                 19                 :                : #include "postgres.h"
                                 20                 :                : 
                                 21                 :                : #include <limits.h>
                                 22                 :                : 
                                 23                 :                : #include "miscadmin.h"
                                 24                 :                : #include "portability/instr_time.h"
                                 25                 :                : #include "storage/condition_variable.h"
                                 26                 :                : #include "storage/proc.h"
                                 27                 :                : #include "storage/proclist.h"
                                 28                 :                : #include "storage/spin.h"
                                 29                 :                : 
                                 30                 :                : /* Initially, we are not prepared to sleep on any condition variable. */
                                 31                 :                : static ConditionVariable *cv_sleep_target = NULL;
                                 32                 :                : 
                                 33                 :                : /*
                                 34                 :                :  * Initialize a condition variable.
                                 35                 :                :  */
                                 36                 :                : void
 3312 rhaas@postgresql.org       37                 :CBC    16666868 : ConditionVariableInit(ConditionVariable *cv)
                                 38                 :                : {
                                 39                 :       16666868 :     SpinLockInit(&cv->mutex);
                                 40                 :       16666868 :     proclist_init(&cv->wakeup);
                                 41                 :       16666868 : }
                                 42                 :                : 
                                 43                 :                : /*
                                 44                 :                :  * Prepare to wait on a given condition variable.
                                 45                 :                :  *
                                 46                 :                :  * This can optionally be called before entering a test/sleep loop.
                                 47                 :                :  * Doing so is more efficient if we'll need to sleep at least once.
                                 48                 :                :  * However, if the first test of the exit condition is likely to succeed,
                                 49                 :                :  * it's more efficient to omit the ConditionVariablePrepareToSleep call.
                                 50                 :                :  * See comments in ConditionVariableSleep for more detail.
                                 51                 :                :  *
                                 52                 :                :  * Caution: "before entering the loop" means you *must* test the exit
                                 53                 :                :  * condition between calling ConditionVariablePrepareToSleep and calling
                                 54                 :                :  * ConditionVariableSleep.  If that is inconvenient, omit calling
                                 55                 :                :  * ConditionVariablePrepareToSleep.
                                 56                 :                :  */
                                 57                 :                : void
                                 58                 :         418668 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
                                 59                 :                : {
  664 heikki.linnakangas@i       60                 :         418668 :     int         pgprocno = MyProcNumber;
                                 61                 :                : 
                                 62                 :                :     /*
                                 63                 :                :      * If some other sleep is already prepared, cancel it; this is necessary
                                 64                 :                :      * because we have just one static variable tracking the prepared sleep,
                                 65                 :                :      * and also only one cvWaitLink in our PGPROC.  It's okay to do this
                                 66                 :                :      * because whenever control does return to the other test-and-sleep loop,
                                 67                 :                :      * its ConditionVariableSleep call will just re-establish that sleep as
                                 68                 :                :      * the prepared one.
                                 69                 :                :      */
 2899 tgl@sss.pgh.pa.us          70         [ +  + ]:         418668 :     if (cv_sleep_target != NULL)
                                 71                 :           2529 :         ConditionVariableCancelSleep();
                                 72                 :                : 
                                 73                 :                :     /* Record the condition variable on which we will sleep. */
 3312 rhaas@postgresql.org       74                 :         418668 :     cv_sleep_target = cv;
                                 75                 :                : 
                                 76                 :                :     /* Add myself to the wait queue. */
                                 77         [ +  + ]:         418668 :     SpinLockAcquire(&cv->mutex);
 2900 tgl@sss.pgh.pa.us          78                 :         418668 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
 3312 rhaas@postgresql.org       79                 :         418668 :     SpinLockRelease(&cv->mutex);
                                 80                 :         418668 : }
                                 81                 :                : 
                                 82                 :                : /*
                                 83                 :                :  * Wait for the given condition variable to be signaled.
                                 84                 :                :  *
                                 85                 :                :  * This should be called in a predicate loop that tests for a specific exit
                                 86                 :                :  * condition and otherwise sleeps, like so:
                                 87                 :                :  *
                                 88                 :                :  *   ConditionVariablePrepareToSleep(cv);  // optional
                                 89                 :                :  *   while (condition for which we are waiting is not true)
                                 90                 :                :  *       ConditionVariableSleep(cv, wait_event_info);
                                 91                 :                :  *   ConditionVariableCancelSleep();
                                 92                 :                :  *
                                 93                 :                :  * wait_event_info should be a value from one of the WaitEventXXX enums
                                 94                 :                :  * defined in pgstat.h.  This controls the contents of pg_stat_activity's
                                 95                 :                :  * wait_event_type and wait_event columns while waiting.
                                 96                 :                :  */
                                 97                 :                : void
                                 98                 :         313311 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
                                 99                 :                : {
 2349 tmunro@postgresql.or      100                 :         313311 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
                                101                 :                :                                        wait_event_info);
                                102                 :         313310 : }
                                103                 :                : 
                                104                 :                : /*
                                105                 :                :  * Wait for a condition variable to be signaled or a timeout to be reached.
                                106                 :                :  *
                                107                 :                :  * The "timeout" is given in milliseconds.
                                108                 :                :  *
                                109                 :                :  * Returns true when timeout expires, otherwise returns false.
                                110                 :                :  *
                                111                 :                :  * See ConditionVariableSleep() for general usage.
                                112                 :                :  */
                                113                 :                : bool
                                114                 :         313814 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
                                115                 :                :                             uint32 wait_event_info)
                                116                 :                : {
                                117                 :         313814 :     long        cur_timeout = -1;
                                118                 :                :     instr_time  start_time;
                                119                 :                :     instr_time  cur_time;
                                120                 :                :     int         wait_events;
                                121                 :                : 
                                122                 :                :     /*
                                123                 :                :      * If the caller didn't prepare to sleep explicitly, then do so now and
                                124                 :                :      * return immediately.  The caller's predicate loop should immediately
                                125                 :                :      * call again if its exit condition is not yet met.  This will result in
                                126                 :                :      * the exit condition being tested twice before we first sleep.  The extra
                                127                 :                :      * test can be prevented by calling ConditionVariablePrepareToSleep(cv)
                                128                 :                :      * first.  Whether it's worth doing that depends on whether you expect the
                                129                 :                :      * exit condition to be met initially, in which case skipping the prepare
                                130                 :                :      * is recommended because it avoids manipulations of the wait list, or not
                                131                 :                :      * met initially, in which case preparing first is better because it
                                132                 :                :      * avoids one extra test of the exit condition.
                                133                 :                :      *
                                134                 :                :      * If we are currently prepared to sleep on some other CV, we just cancel
                                135                 :                :      * that and prepare this one; see ConditionVariablePrepareToSleep.
                                136                 :                :      */
 2899 tgl@sss.pgh.pa.us         137         [ +  + ]:         313814 :     if (cv_sleep_target != cv)
                                138                 :                :     {
 3312 rhaas@postgresql.org      139                 :            337 :         ConditionVariablePrepareToSleep(cv);
 2349 tmunro@postgresql.or      140                 :            337 :         return false;
                                141                 :                :     }
                                142                 :                : 
                                143                 :                :     /*
                                144                 :                :      * Record the current time so that we can calculate the remaining timeout
                                145                 :                :      * if we are woken up spuriously.
                                146                 :                :      */
                                147         [ +  + ]:         313477 :     if (timeout >= 0)
                                148                 :                :     {
                                149                 :            252 :         INSTR_TIME_SET_CURRENT(start_time);
                                150   [ +  -  -  + ]:            252 :         Assert(timeout >= 0 && timeout <= INT_MAX);
                                151                 :            252 :         cur_timeout = timeout;
 1966                           152                 :            252 :         wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
                                153                 :                :     }
                                154                 :                :     else
                                155                 :         313225 :         wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
                                156                 :                : 
                                157                 :                :     while (true)
 2349                           158                 :           3244 :     {
                                159                 :         316721 :         bool        done = false;
                                160                 :                : 
                                161                 :                :         /*
                                162                 :                :          * Wait for latch to be set.  (If we're awakened for some other
                                163                 :                :          * reason, the code below will cope anyway.)
                                164                 :                :          */
 1966                           165                 :         316721 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
                                166                 :                : 
                                167                 :                :         /* Reset latch before examining the state of the wait list. */
 3116 andres@anarazel.de        168                 :         316721 :         ResetLatch(MyLatch);
                                169                 :                : 
                                170                 :                :         /*
                                171                 :                :          * If this process has been taken out of the wait list, then we know
                                172                 :                :          * that it has been signaled by ConditionVariableSignal (or
                                173                 :                :          * ConditionVariableBroadcast), so we should return to the caller. But
                                174                 :                :          * that doesn't guarantee that the exit condition is met, only that we
                                175                 :                :          * ought to check it.  So we must put the process back into the wait
                                176                 :                :          * list, to ensure we don't miss any additional wakeup occurring while
                                177                 :                :          * the caller checks its exit condition.  We can take ourselves out of
                                178                 :                :          * the wait list only when the caller calls
                                179                 :                :          * ConditionVariableCancelSleep.
                                180                 :                :          *
                                181                 :                :          * If we're still in the wait list, then the latch must have been set
                                182                 :                :          * by something other than ConditionVariableSignal; though we don't
                                183                 :                :          * guarantee not to return spuriously, we'll avoid this obvious case.
                                184                 :                :          */
 3312 rhaas@postgresql.org      185         [ +  + ]:         316721 :         SpinLockAcquire(&cv->mutex);
  664 heikki.linnakangas@i      186         [ +  + ]:         316721 :         if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
                                187                 :                :         {
 3312 rhaas@postgresql.org      188                 :         313396 :             done = true;
  664 heikki.linnakangas@i      189                 :         313396 :             proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
                                190                 :                :         }
 3312 rhaas@postgresql.org      191                 :         316721 :         SpinLockRelease(&cv->mutex);
                                192                 :                : 
                                193                 :                :         /*
                                194                 :                :          * Check for interrupts, and return spuriously if that caused the
                                195                 :                :          * current sleep target to change (meaning that interrupt handler code
                                196                 :                :          * waited for a different condition variable).
                                197                 :                :          */
 1752 tmunro@postgresql.or      198         [ +  + ]:         316721 :         CHECK_FOR_INTERRUPTS();
                                199         [ +  + ]:         316720 :         if (cv != cv_sleep_target)
                                200                 :             81 :             done = true;
                                201                 :                : 
                                202                 :                :         /* We were signaled, so return */
 2349                           203         [ +  + ]:         316720 :         if (done)
                                204                 :         313473 :             return false;
                                205                 :                : 
                                206                 :                :         /* If we're not done, update cur_timeout for next iteration */
                                207         [ +  + ]:           3247 :         if (timeout >= 0)
                                208                 :                :         {
                                209                 :             31 :             INSTR_TIME_SET_CURRENT(cur_time);
                                210                 :             31 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
                                211                 :             31 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
                                212                 :                : 
                                213                 :                :             /* Have we crossed the timeout threshold? */
                                214         [ +  + ]:             31 :             if (cur_timeout <= 0)
                                215                 :              3 :                 return true;
                                216                 :                :         }
                                217                 :                :     }
                                218                 :                : }
                                219                 :                : 
                                220                 :                : /*
                                221                 :                :  * Cancel any pending sleep operation.
                                222                 :                :  *
                                223                 :                :  * We just need to remove ourselves from the wait queue of any condition
                                224                 :                :  * variable for which we have previously prepared a sleep.
                                225                 :                :  *
                                226                 :                :  * Do nothing if nothing is pending; this allows this function to be called
                                227                 :                :  * during transaction abort to clean up any unfinished CV sleep.
                                228                 :                :  *
                                229                 :                :  * Return true if we've been signaled.
                                230                 :                :  */
                                231                 :                : bool
 3312 rhaas@postgresql.org      232                 :         485150 : ConditionVariableCancelSleep(void)
                                233                 :                : {
                                234                 :         485150 :     ConditionVariable *cv = cv_sleep_target;
 2349 tmunro@postgresql.or      235                 :         485150 :     bool        signaled = false;
                                236                 :                : 
 3312 rhaas@postgresql.org      237         [ +  + ]:         485150 :     if (cv == NULL)
  855 tmunro@postgresql.or      238                 :          66482 :         return false;
                                239                 :                : 
 3312 rhaas@postgresql.org      240         [ +  + ]:         418668 :     SpinLockAcquire(&cv->mutex);
  664 heikki.linnakangas@i      241         [ +  + ]:         418668 :     if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
                                242                 :         394820 :         proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
                                243                 :                :     else
 2349 tmunro@postgresql.or      244                 :          23848 :         signaled = true;
 3312 rhaas@postgresql.org      245                 :         418668 :     SpinLockRelease(&cv->mutex);
                                246                 :                : 
                                247                 :         418668 :     cv_sleep_target = NULL;
                                248                 :                : 
  855 tmunro@postgresql.or      249                 :         418668 :     return signaled;
                                250                 :                : }
                                251                 :                : 
                                252                 :                : /*
                                253                 :                :  * Wake up the oldest process sleeping on the CV, if there is any.
                                254                 :                :  *
                                255                 :                :  * Note: it's difficult to tell whether this has any real effect: we know
                                256                 :                :  * whether we took an entry off the list, but the entry might only be a
                                257                 :                :  * sentinel.  Hence, think twice before proposing that this should return
                                258                 :                :  * a flag telling whether it woke somebody.
                                259                 :                :  */
                                260                 :                : void
 3312 rhaas@postgresql.org      261                 :            832 : ConditionVariableSignal(ConditionVariable *cv)
                                262                 :                : {
 3136 bruce@momjian.us          263                 :            832 :     PGPROC     *proc = NULL;
                                264                 :                : 
                                265                 :                :     /* Remove the first process from the wakeup queue (if any). */
 3312 rhaas@postgresql.org      266         [ -  + ]:            832 :     SpinLockAcquire(&cv->mutex);
                                267         [ +  + ]:            832 :     if (!proclist_is_empty(&cv->wakeup))
                                268                 :             75 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                269                 :            832 :     SpinLockRelease(&cv->mutex);
                                270                 :                : 
                                271                 :                :     /* If we found someone sleeping, set their latch to wake them up. */
                                272         [ +  + ]:            832 :     if (proc != NULL)
                                273                 :             75 :         SetLatch(&proc->procLatch);
                                274                 :            832 : }
                                275                 :                : 
                                276                 :                : /*
                                277                 :                :  * Wake up all processes sleeping on the given CV.
                                278                 :                :  *
                                279                 :                :  * This guarantees to wake all processes that were sleeping on the CV
                                280                 :                :  * at time of call, but processes that add themselves to the list mid-call
                                281                 :                :  * will typically not get awakened.
                                282                 :                :  */
                                283                 :                : void
                                284                 :        6505918 : ConditionVariableBroadcast(ConditionVariable *cv)
                                285                 :                : {
  664 heikki.linnakangas@i      286                 :        6505918 :     int         pgprocno = MyProcNumber;
 2903 tgl@sss.pgh.pa.us         287                 :        6505918 :     PGPROC     *proc = NULL;
                                288                 :        6505918 :     bool        have_sentinel = false;
                                289                 :                : 
                                290                 :                :     /*
                                291                 :                :      * In some use-cases, it is common for awakened processes to immediately
                                292                 :                :      * re-queue themselves.  If we just naively try to reduce the wakeup list
                                293                 :                :      * to empty, we'll get into a potentially-indefinite loop against such a
                                294                 :                :      * process.  The semantics we really want are just to be sure that we have
                                295                 :                :      * wakened all processes that were in the list at entry.  We can use our
                                296                 :                :      * own cvWaitLink as a sentinel to detect when we've finished.
                                297                 :                :      *
                                298                 :                :      * A seeming flaw in this approach is that someone else might signal the
                                299                 :                :      * CV and in doing so remove our sentinel entry.  But that's fine: since
                                300                 :                :      * CV waiters are always added and removed in order, that must mean that
                                301                 :                :      * every previous waiter has been wakened, so we're done.  We'll get an
                                302                 :                :      * extra "set" on our latch from the someone else's signal, which is
                                303                 :                :      * slightly inefficient but harmless.
                                304                 :                :      *
                                305                 :                :      * We can't insert our cvWaitLink as a sentinel if it's already in use in
                                306                 :                :      * some other proclist.  While that's not expected to be true for typical
                                307                 :                :      * uses of this function, we can deal with it by simply canceling any
                                308                 :                :      * prepared CV sleep.  The next call to ConditionVariableSleep will take
                                309                 :                :      * care of re-establishing the lost state.
                                310                 :                :      */
 2899                           311         [ +  + ]:        6505918 :     if (cv_sleep_target != NULL)
                                312                 :             89 :         ConditionVariableCancelSleep();
                                313                 :                : 
                                314                 :                :     /*
                                315                 :                :      * Inspect the state of the queue.  If it's empty, we have nothing to do.
                                316                 :                :      * If there's exactly one entry, we need only remove and signal that
                                317                 :                :      * entry.  Otherwise, remove the first entry and insert our sentinel.
                                318                 :                :      */
 2903                           319         [ +  + ]:        6505918 :     SpinLockAcquire(&cv->mutex);
                                320                 :                :     /* While we're here, let's assert we're not in the list. */
                                321         [ -  + ]:        6505918 :     Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
                                322                 :                : 
                                323         [ +  + ]:        6505918 :     if (!proclist_is_empty(&cv->wakeup))
                                324                 :                :     {
                                325                 :         290745 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                326         [ +  + ]:         290745 :         if (!proclist_is_empty(&cv->wakeup))
                                327                 :                :         {
                                328                 :           1974 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
                                329                 :           1974 :             have_sentinel = true;
                                330                 :                :         }
                                331                 :                :     }
                                332                 :        6505918 :     SpinLockRelease(&cv->mutex);
                                333                 :                : 
                                334                 :                :     /* Awaken first waiter, if there was one. */
                                335         [ +  + ]:        6505918 :     if (proc != NULL)
                                336                 :         290745 :         SetLatch(&proc->procLatch);
                                337                 :                : 
                                338         [ +  + ]:        6510000 :     while (have_sentinel)
                                339                 :                :     {
                                340                 :                :         /*
                                341                 :                :          * Each time through the loop, remove the first wakeup list entry, and
                                342                 :                :          * signal it unless it's our sentinel.  Repeat as long as the sentinel
                                343                 :                :          * remains in the list.
                                344                 :                :          *
                                345                 :                :          * Notice that if someone else removes our sentinel, we will waken one
                                346                 :                :          * additional process before exiting.  That's intentional, because if
                                347                 :                :          * someone else signals the CV, they may be intending to waken some
                                348                 :                :          * third process that added itself to the list after we added the
                                349                 :                :          * sentinel.  Better to give a spurious wakeup (which should be
                                350                 :                :          * harmless beyond wasting some cycles) than to lose a wakeup.
                                351                 :                :          */
                                352                 :           4082 :         proc = NULL;
                                353         [ +  + ]:           4082 :         SpinLockAcquire(&cv->mutex);
                                354         [ +  + ]:           4082 :         if (!proclist_is_empty(&cv->wakeup))
                                355                 :           4070 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                356                 :           4082 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
                                357                 :           4082 :         SpinLockRelease(&cv->mutex);
                                358                 :                : 
                                359   [ +  +  +  + ]:           4082 :         if (proc != NULL && proc != MyProc)
                                360                 :           2111 :             SetLatch(&proc->procLatch);
                                361                 :                :     }
 3312 rhaas@postgresql.org      362                 :        6505918 : }
        

Generated by: LCOV version 2.4-beta