LCOV - code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 100.0 % 92 92
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 100.0 % 42 42

             Branch data     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-2026, 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
      37                 :    19669723 : ConditionVariableInit(ConditionVariable *cv)
      38                 :             : {
      39                 :    19669723 :     SpinLockInit(&cv->mutex);
      40                 :    19669723 :     proclist_init(&cv->wakeup);
      41                 :    19669723 : }
      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                 :      478962 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
      59                 :             : {
      60                 :      478962 :     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                 :             :      */
      70         [ +  + ]:      478962 :     if (cv_sleep_target != NULL)
      71                 :          44 :         ConditionVariableCancelSleep();
      72                 :             : 
      73                 :             :     /* Record the condition variable on which we will sleep. */
      74                 :      478962 :     cv_sleep_target = cv;
      75                 :             : 
      76                 :             :     /* Add myself to the wait queue. */
      77                 :      478962 :     SpinLockAcquire(&cv->mutex);
      78                 :      478962 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
      79                 :      478962 :     SpinLockRelease(&cv->mutex);
      80                 :      478962 : }
      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                 :      380703 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
      99                 :             : {
     100                 :      380703 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
     101                 :             :                                        wait_event_info);
     102                 :      380697 : }
     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                 :      383636 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
     115                 :             :                             uint32 wait_event_info)
     116                 :             : {
     117                 :      383636 :     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                 :             :      */
     137         [ +  + ]:      383636 :     if (cv_sleep_target != cv)
     138                 :             :     {
     139                 :        1607 :         ConditionVariablePrepareToSleep(cv);
     140                 :        1607 :         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         [ +  + ]:      382029 :     if (timeout >= 0)
     148                 :             :     {
     149                 :        1468 :         INSTR_TIME_SET_CURRENT(start_time);
     150                 :             :         Assert(timeout >= 0 && timeout <= INT_MAX);
     151                 :        1468 :         cur_timeout = timeout;
     152                 :        1468 :         wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
     153                 :             :     }
     154                 :             :     else
     155                 :      380561 :         wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
     156                 :             : 
     157                 :             :     while (true)
     158                 :        3576 :     {
     159                 :      385605 :         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                 :             :          */
     165                 :      385605 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
     166                 :             : 
     167                 :             :         /* Reset latch before examining the state of the wait list. */
     168                 :      385603 :         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                 :             :          */
     185                 :      385603 :         SpinLockAcquire(&cv->mutex);
     186         [ +  + ]:      385603 :         if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
     187                 :             :         {
     188                 :      381408 :             done = true;
     189                 :      381408 :             proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
     190                 :             :         }
     191                 :      385603 :         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                 :             :          */
     198         [ +  + ]:      385603 :         CHECK_FOR_INTERRUPTS();
     199         [ +  + ]:      385599 :         if (cv != cv_sleep_target)
     200                 :         614 :             done = true;
     201                 :             : 
     202                 :             :         /* We were signaled, so return */
     203         [ +  + ]:      385599 :         if (done)
     204                 :      382017 :             return false;
     205                 :             : 
     206                 :             :         /* If we're not done, update cur_timeout for next iteration */
     207         [ +  + ]:        3582 :         if (timeout >= 0)
     208                 :             :         {
     209                 :          85 :             INSTR_TIME_SET_CURRENT(cur_time);
     210                 :          85 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
     211                 :          85 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
     212                 :             : 
     213                 :             :             /* Have we crossed the timeout threshold? */
     214         [ +  + ]:          85 :             if (cur_timeout <= 0)
     215                 :           6 :                 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
     232                 :      619373 : ConditionVariableCancelSleep(void)
     233                 :             : {
     234                 :      619373 :     ConditionVariable *cv = cv_sleep_target;
     235                 :      619373 :     bool        signaled = false;
     236                 :             : 
     237         [ +  + ]:      619373 :     if (cv == NULL)
     238                 :      140411 :         return false;
     239                 :             : 
     240                 :      478962 :     SpinLockAcquire(&cv->mutex);
     241         [ +  + ]:      478962 :     if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
     242                 :      454580 :         proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
     243                 :             :     else
     244                 :       24382 :         signaled = true;
     245                 :      478962 :     SpinLockRelease(&cv->mutex);
     246                 :             : 
     247                 :      478962 :     cv_sleep_target = NULL;
     248                 :             : 
     249                 :      478962 :     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
     261                 :        1201 : ConditionVariableSignal(ConditionVariable *cv)
     262                 :             : {
     263                 :        1201 :     PGPROC     *proc = NULL;
     264                 :             : 
     265                 :             :     /* Remove the first process from the wakeup queue (if any). */
     266                 :        1201 :     SpinLockAcquire(&cv->mutex);
     267         [ +  + ]:        1201 :     if (!proclist_is_empty(&cv->wakeup))
     268                 :         147 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     269                 :        1201 :     SpinLockRelease(&cv->mutex);
     270                 :             : 
     271                 :             :     /* If we found someone sleeping, set their latch to wake them up. */
     272         [ +  + ]:        1201 :     if (proc != NULL)
     273                 :         147 :         SetLatch(&proc->procLatch);
     274                 :        1201 : }
     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                 :     7230981 : ConditionVariableBroadcast(ConditionVariable *cv)
     285                 :             : {
     286                 :     7230981 :     int         pgprocno = MyProcNumber;
     287                 :     7230981 :     PGPROC     *proc = NULL;
     288                 :     7230981 :     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                 :             :      */
     311         [ +  + ]:     7230981 :     if (cv_sleep_target != NULL)
     312                 :         625 :         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                 :             :      */
     319                 :     7230981 :     SpinLockAcquire(&cv->mutex);
     320                 :             :     /* While we're here, let's assert we're not in the list. */
     321                 :             :     Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
     322                 :             : 
     323         [ +  + ]:     7230981 :     if (!proclist_is_empty(&cv->wakeup))
     324                 :             :     {
     325                 :      345505 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     326         [ +  + ]:      345505 :         if (!proclist_is_empty(&cv->wakeup))
     327                 :             :         {
     328                 :        1754 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
     329                 :        1754 :             have_sentinel = true;
     330                 :             :         }
     331                 :             :     }
     332                 :     7230981 :     SpinLockRelease(&cv->mutex);
     333                 :             : 
     334                 :             :     /* Awaken first waiter, if there was one. */
     335         [ +  + ]:     7230981 :     if (proc != NULL)
     336                 :      345505 :         SetLatch(&proc->procLatch);
     337                 :             : 
     338         [ +  + ]:     7234618 :     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                 :        3637 :         proc = NULL;
     353                 :        3637 :         SpinLockAcquire(&cv->mutex);
     354         [ +  + ]:        3637 :         if (!proclist_is_empty(&cv->wakeup))
     355                 :        3624 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     356                 :        3637 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
     357                 :        3637 :         SpinLockRelease(&cv->mutex);
     358                 :             : 
     359   [ +  +  +  + ]:        3637 :         if (proc != NULL && proc != MyProc)
     360                 :        1889 :             SetLatch(&proc->procLatch);
     361                 :             :     }
     362                 :     7230981 : }
        

Generated by: LCOV version 2.0-1