LCOV - code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 100.0 % 92 92
Test Date: 2026-03-03 14:15:12 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            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     17995666 : ConditionVariableInit(ConditionVariable *cv)
      38              : {
      39     17995666 :     SpinLockInit(&cv->mutex);
      40     17995666 :     proclist_init(&cv->wakeup);
      41     17995666 : }
      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       404341 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
      59              : {
      60       404341 :     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       404341 :     if (cv_sleep_target != NULL)
      71         2448 :         ConditionVariableCancelSleep();
      72              : 
      73              :     /* Record the condition variable on which we will sleep. */
      74       404341 :     cv_sleep_target = cv;
      75              : 
      76              :     /* Add myself to the wait queue. */
      77       404341 :     SpinLockAcquire(&cv->mutex);
      78       404341 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
      79       404341 :     SpinLockRelease(&cv->mutex);
      80       404341 : }
      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       294168 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
      99              : {
     100       294168 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
     101              :                                        wait_event_info);
     102       294166 : }
     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       296981 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
     115              :                             uint32 wait_event_info)
     116              : {
     117       296981 :     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       296981 :     if (cv_sleep_target != cv)
     138              :     {
     139         1492 :         ConditionVariablePrepareToSleep(cv);
     140         1492 :         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       295489 :     if (timeout >= 0)
     148              :     {
     149         1406 :         INSTR_TIME_SET_CURRENT(start_time);
     150              :         Assert(timeout >= 0 && timeout <= INT_MAX);
     151         1406 :         cur_timeout = timeout;
     152         1406 :         wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
     153              :     }
     154              :     else
     155       294083 :         wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
     156              : 
     157              :     while (true)
     158         3226 :     {
     159       298715 :         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       298715 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
     166              : 
     167              :         /* Reset latch before examining the state of the wait list. */
     168       298715 :         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       298715 :         SpinLockAcquire(&cv->mutex);
     186       298715 :         if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
     187              :         {
     188       294950 :             done = true;
     189       294950 :             proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
     190              :         }
     191       298715 :         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       298715 :         CHECK_FOR_INTERRUPTS();
     199       298713 :         if (cv != cv_sleep_target)
     200          538 :             done = true;
     201              : 
     202              :         /* We were signaled, so return */
     203       298713 :         if (done)
     204       295484 :             return false;
     205              : 
     206              :         /* If we're not done, update cur_timeout for next iteration */
     207         3229 :         if (timeout >= 0)
     208              :         {
     209           56 :             INSTR_TIME_SET_CURRENT(cur_time);
     210           56 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
     211           56 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
     212              : 
     213              :             /* Have we crossed the timeout threshold? */
     214           56 :             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
     232       523924 : ConditionVariableCancelSleep(void)
     233              : {
     234       523924 :     ConditionVariable *cv = cv_sleep_target;
     235       523924 :     bool        signaled = false;
     236              : 
     237       523924 :     if (cv == NULL)
     238       119583 :         return false;
     239              : 
     240       404341 :     SpinLockAcquire(&cv->mutex);
     241       404341 :     if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
     242       381186 :         proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
     243              :     else
     244        23155 :         signaled = true;
     245       404341 :     SpinLockRelease(&cv->mutex);
     246              : 
     247       404341 :     cv_sleep_target = NULL;
     248              : 
     249       404341 :     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          880 : ConditionVariableSignal(ConditionVariable *cv)
     262              : {
     263          880 :     PGPROC     *proc = NULL;
     264              : 
     265              :     /* Remove the first process from the wakeup queue (if any). */
     266          880 :     SpinLockAcquire(&cv->mutex);
     267          880 :     if (!proclist_is_empty(&cv->wakeup))
     268           87 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     269          880 :     SpinLockRelease(&cv->mutex);
     270              : 
     271              :     /* If we found someone sleeping, set their latch to wake them up. */
     272          880 :     if (proc != NULL)
     273           87 :         SetLatch(&proc->procLatch);
     274          880 : }
     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      6538981 : ConditionVariableBroadcast(ConditionVariable *cv)
     285              : {
     286      6538981 :     int         pgprocno = MyProcNumber;
     287      6538981 :     PGPROC     *proc = NULL;
     288      6538981 :     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      6538981 :     if (cv_sleep_target != NULL)
     312          548 :         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      6538981 :     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      6538981 :     if (!proclist_is_empty(&cv->wakeup))
     324              :     {
     325       268996 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     326       268996 :         if (!proclist_is_empty(&cv->wakeup))
     327              :         {
     328         1517 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
     329         1517 :             have_sentinel = true;
     330              :         }
     331              :     }
     332      6538981 :     SpinLockRelease(&cv->mutex);
     333              : 
     334              :     /* Awaken first waiter, if there was one. */
     335      6538981 :     if (proc != NULL)
     336       268996 :         SetLatch(&proc->procLatch);
     337              : 
     338      6542143 :     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         3162 :         proc = NULL;
     353         3162 :         SpinLockAcquire(&cv->mutex);
     354         3162 :         if (!proclist_is_empty(&cv->wakeup))
     355         3144 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     356         3162 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
     357         3162 :         SpinLockRelease(&cv->mutex);
     358              : 
     359         3162 :         if (proc != NULL && proc != MyProc)
     360         1650 :             SetLatch(&proc->procLatch);
     361              :     }
     362      6538981 : }
        

Generated by: LCOV version 2.0-1