LCOV - differential code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage HEAD vs 15 Lines: 98.9 % 95 94 1 94
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 7 7 7
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 98.9 % 95 94 1 94
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 100.0 % 7 7 7

 Age         Owner                  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-2023, 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 "miscadmin.h"
                                 22                 : #include "portability/instr_time.h"
                                 23                 : #include "storage/condition_variable.h"
                                 24                 : #include "storage/ipc.h"
                                 25                 : #include "storage/proc.h"
                                 26                 : #include "storage/proclist.h"
                                 27                 : #include "storage/spin.h"
                                 28                 : #include "utils/memutils.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
 2329 rhaas                      37 CBC    20123130 : ConditionVariableInit(ConditionVariable *cv)
                                 38                 : {
                                 39        20123130 :     SpinLockInit(&cv->mutex);
                                 40        20123130 :     proclist_init(&cv->wakeup);
                                 41        20123130 : }
                                 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            4215 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
                                 59                 : {
 2153 bruce                      60            4215 :     int         pgprocno = MyProc->pgprocno;
                                 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                 :      */
 1916 tgl                        70            4215 :     if (cv_sleep_target != NULL)
 1916 tgl                        71 UBC           0 :         ConditionVariableCancelSleep();
                                 72                 : 
                                 73                 :     /* Record the condition variable on which we will sleep. */
 2329 rhaas                      74 CBC        4215 :     cv_sleep_target = cv;
                                 75                 : 
                                 76                 :     /* Add myself to the wait queue. */
                                 77            4215 :     SpinLockAcquire(&cv->mutex);
 1917 tgl                        78            4215 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
 2329 rhaas                      79            4215 :     SpinLockRelease(&cv->mutex);
                                 80            4215 : }
                                 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            2373 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
                                 99                 : {
 1366 tmunro                    100            2373 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
                                101                 :                                        wait_event_info);
                                102            2373 : }
                                103                 : 
                                104                 : /*
                                105                 :  * Wait for a condition variable to be signaled or a timeout to be reached.
                                106                 :  *
                                107                 :  * Returns true when timeout expires, otherwise returns false.
                                108                 :  *
                                109                 :  * See ConditionVariableSleep() for general usage.
                                110                 :  */
                                111                 : bool
                                112            2555 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
                                113                 :                             uint32 wait_event_info)
                                114                 : {
                                115            2555 :     long        cur_timeout = -1;
                                116                 :     instr_time  start_time;
                                117                 :     instr_time  cur_time;
                                118                 :     int         wait_events;
                                119                 : 
                                120                 :     /*
                                121                 :      * If the caller didn't prepare to sleep explicitly, then do so now and
                                122                 :      * return immediately.  The caller's predicate loop should immediately
                                123                 :      * call again if its exit condition is not yet met.  This will result in
                                124                 :      * the exit condition being tested twice before we first sleep.  The extra
                                125                 :      * test can be prevented by calling ConditionVariablePrepareToSleep(cv)
                                126                 :      * first.  Whether it's worth doing that depends on whether you expect the
                                127                 :      * exit condition to be met initially, in which case skipping the prepare
                                128                 :      * is recommended because it avoids manipulations of the wait list, or not
                                129                 :      * met initially, in which case preparing first is better because it
                                130                 :      * avoids one extra test of the exit condition.
                                131                 :      *
                                132                 :      * If we are currently prepared to sleep on some other CV, we just cancel
                                133                 :      * that and prepare this one; see ConditionVariablePrepareToSleep.
                                134                 :      */
 1916 tgl                       135            2555 :     if (cv_sleep_target != cv)
                                136                 :     {
 2329 rhaas                     137             153 :         ConditionVariablePrepareToSleep(cv);
 1366 tmunro                    138             153 :         return false;
                                139                 :     }
                                140                 : 
                                141                 :     /*
                                142                 :      * Record the current time so that we can calculate the remaining timeout
                                143                 :      * if we are woken up spuriously.
                                144                 :      */
                                145            2402 :     if (timeout >= 0)
                                146                 :     {
                                147              91 :         INSTR_TIME_SET_CURRENT(start_time);
                                148              91 :         Assert(timeout >= 0 && timeout <= INT_MAX);
                                149              91 :         cur_timeout = timeout;
  983                           150              91 :         wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
                                151                 :     }
                                152                 :     else
                                153            2311 :         wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
                                154                 : 
                                155                 :     while (true)
 1366                           156             627 :     {
                                157            3029 :         bool        done = false;
                                158                 : 
                                159                 :         /*
                                160                 :          * Wait for latch to be set.  (If we're awakened for some other
                                161                 :          * reason, the code below will cope anyway.)
                                162                 :          */
  983                           163            3029 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
                                164                 : 
                                165                 :         /* Reset latch before examining the state of the wait list. */
 2133 andres                    166            3029 :         ResetLatch(MyLatch);
                                167                 : 
                                168                 :         /*
                                169                 :          * If this process has been taken out of the wait list, then we know
                                170                 :          * that it has been signaled by ConditionVariableSignal (or
                                171                 :          * ConditionVariableBroadcast), so we should return to the caller. But
                                172                 :          * that doesn't guarantee that the exit condition is met, only that we
                                173                 :          * ought to check it.  So we must put the process back into the wait
                                174                 :          * list, to ensure we don't miss any additional wakeup occurring while
                                175                 :          * the caller checks its exit condition.  We can take ourselves out of
                                176                 :          * the wait list only when the caller calls
                                177                 :          * ConditionVariableCancelSleep.
                                178                 :          *
                                179                 :          * If we're still in the wait list, then the latch must have been set
                                180                 :          * by something other than ConditionVariableSignal; though we don't
                                181                 :          * guarantee not to return spuriously, we'll avoid this obvious case.
                                182                 :          */
 2329 rhaas                     183            3029 :         SpinLockAcquire(&cv->mutex);
                                184            3029 :         if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
                                185                 :         {
                                186            2362 :             done = true;
                                187            2362 :             proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
                                188                 :         }
                                189            3029 :         SpinLockRelease(&cv->mutex);
                                190                 : 
                                191                 :         /*
                                192                 :          * Check for interrupts, and return spuriously if that caused the
                                193                 :          * current sleep target to change (meaning that interrupt handler code
                                194                 :          * waited for a different condition variable).
                                195                 :          */
  769 tmunro                    196            3029 :         CHECK_FOR_INTERRUPTS();
                                197            3029 :         if (cv != cv_sleep_target)
                                198              38 :             done = true;
                                199                 : 
                                200                 :         /* We were signaled, so return */
 1366                           201            3029 :         if (done)
                                202            2400 :             return false;
                                203                 : 
                                204                 :         /* If we're not done, update cur_timeout for next iteration */
                                205             629 :         if (timeout >= 0)
                                206                 :         {
                                207              24 :             INSTR_TIME_SET_CURRENT(cur_time);
                                208              24 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
                                209              24 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
                                210                 : 
                                211                 :             /* Have we crossed the timeout threshold? */
                                212              24 :             if (cur_timeout <= 0)
                                213               2 :                 return true;
                                214                 :         }
                                215                 :     }
                                216                 : }
                                217                 : 
                                218                 : /*
                                219                 :  * Cancel any pending sleep operation.
                                220                 :  *
                                221                 :  * We just need to remove ourselves from the wait queue of any condition
                                222                 :  * variable for which we have previously prepared a sleep.
                                223                 :  *
                                224                 :  * Do nothing if nothing is pending; this allows this function to be called
                                225                 :  * during transaction abort to clean up any unfinished CV sleep.
                                226                 :  */
                                227                 : void
 2329 rhaas                     228           48766 : ConditionVariableCancelSleep(void)
                                229                 : {
                                230           48766 :     ConditionVariable *cv = cv_sleep_target;
 1366 tmunro                    231           48766 :     bool        signaled = false;
                                232                 : 
 2329 rhaas                     233           48766 :     if (cv == NULL)
                                234           44551 :         return;
                                235                 : 
                                236            4215 :     SpinLockAcquire(&cv->mutex);
                                237            4215 :     if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
                                238            4156 :         proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
                                239                 :     else
 1366 tmunro                    240              59 :         signaled = true;
 2329 rhaas                     241            4215 :     SpinLockRelease(&cv->mutex);
                                242                 : 
                                243                 :     /*
                                244                 :      * If we've received a signal, pass it on to another waiting process, if
                                245                 :      * there is one.  Otherwise a call to ConditionVariableSignal() might get
                                246                 :      * lost, despite there being another process ready to handle it.
                                247                 :      */
 1366 tmunro                    248            4215 :     if (signaled)
                                249              59 :         ConditionVariableSignal(cv);
                                250                 : 
 2329 rhaas                     251            4215 :     cv_sleep_target = NULL;
                                252                 : }
                                253                 : 
                                254                 : /*
                                255                 :  * Wake up the oldest process sleeping on the CV, if there is any.
                                256                 :  *
                                257                 :  * Note: it's difficult to tell whether this has any real effect: we know
                                258                 :  * whether we took an entry off the list, but the entry might only be a
                                259                 :  * sentinel.  Hence, think twice before proposing that this should return
                                260                 :  * a flag telling whether it woke somebody.
                                261                 :  */
                                262                 : void
                                263             845 : ConditionVariableSignal(ConditionVariable *cv)
                                264                 : {
 2153 bruce                     265             845 :     PGPROC     *proc = NULL;
                                266                 : 
                                267                 :     /* Remove the first process from the wakeup queue (if any). */
 2329 rhaas                     268             845 :     SpinLockAcquire(&cv->mutex);
                                269             845 :     if (!proclist_is_empty(&cv->wakeup))
                                270              61 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                271             845 :     SpinLockRelease(&cv->mutex);
                                272                 : 
                                273                 :     /* If we found someone sleeping, set their latch to wake them up. */
                                274             845 :     if (proc != NULL)
                                275              61 :         SetLatch(&proc->procLatch);
                                276             845 : }
                                277                 : 
                                278                 : /*
                                279                 :  * Wake up all processes sleeping on the given CV.
                                280                 :  *
                                281                 :  * This guarantees to wake all processes that were sleeping on the CV
                                282                 :  * at time of call, but processes that add themselves to the list mid-call
                                283                 :  * will typically not get awakened.
                                284                 :  */
                                285                 : void
                                286         2615341 : ConditionVariableBroadcast(ConditionVariable *cv)
                                287                 : {
 1920 tgl                       288         2615341 :     int         pgprocno = MyProc->pgprocno;
                                289         2615341 :     PGPROC     *proc = NULL;
                                290         2615341 :     bool        have_sentinel = false;
                                291                 : 
                                292                 :     /*
                                293                 :      * In some use-cases, it is common for awakened processes to immediately
                                294                 :      * re-queue themselves.  If we just naively try to reduce the wakeup list
                                295                 :      * to empty, we'll get into a potentially-indefinite loop against such a
                                296                 :      * process.  The semantics we really want are just to be sure that we have
                                297                 :      * wakened all processes that were in the list at entry.  We can use our
                                298                 :      * own cvWaitLink as a sentinel to detect when we've finished.
                                299                 :      *
                                300                 :      * A seeming flaw in this approach is that someone else might signal the
                                301                 :      * CV and in doing so remove our sentinel entry.  But that's fine: since
                                302                 :      * CV waiters are always added and removed in order, that must mean that
                                303                 :      * every previous waiter has been wakened, so we're done.  We'll get an
                                304                 :      * extra "set" on our latch from the someone else's signal, which is
                                305                 :      * slightly inefficient but harmless.
                                306                 :      *
                                307                 :      * We can't insert our cvWaitLink as a sentinel if it's already in use in
                                308                 :      * some other proclist.  While that's not expected to be true for typical
                                309                 :      * uses of this function, we can deal with it by simply canceling any
                                310                 :      * prepared CV sleep.  The next call to ConditionVariableSleep will take
                                311                 :      * care of re-establishing the lost state.
                                312                 :      */
 1916                           313         2615341 :     if (cv_sleep_target != NULL)
                                314              46 :         ConditionVariableCancelSleep();
                                315                 : 
                                316                 :     /*
                                317                 :      * Inspect the state of the queue.  If it's empty, we have nothing to do.
                                318                 :      * If there's exactly one entry, we need only remove and signal that
                                319                 :      * entry.  Otherwise, remove the first entry and insert our sentinel.
                                320                 :      */
 1920                           321         2615341 :     SpinLockAcquire(&cv->mutex);
                                322                 :     /* While we're here, let's assert we're not in the list. */
                                323         2615341 :     Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
                                324                 : 
                                325         2615341 :     if (!proclist_is_empty(&cv->wakeup))
                                326                 :     {
                                327            1832 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                328            1832 :         if (!proclist_is_empty(&cv->wakeup))
                                329                 :         {
                                330             247 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
                                331             247 :             have_sentinel = true;
                                332                 :         }
                                333                 :     }
                                334         2615341 :     SpinLockRelease(&cv->mutex);
                                335                 : 
                                336                 :     /* Awaken first waiter, if there was one. */
                                337         2615341 :     if (proc != NULL)
                                338            1832 :         SetLatch(&proc->procLatch);
                                339                 : 
                                340         2615904 :     while (have_sentinel)
                                341                 :     {
                                342                 :         /*
                                343                 :          * Each time through the loop, remove the first wakeup list entry, and
                                344                 :          * signal it unless it's our sentinel.  Repeat as long as the sentinel
                                345                 :          * remains in the list.
                                346                 :          *
                                347                 :          * Notice that if someone else removes our sentinel, we will waken one
                                348                 :          * additional process before exiting.  That's intentional, because if
                                349                 :          * someone else signals the CV, they may be intending to waken some
                                350                 :          * third process that added itself to the list after we added the
                                351                 :          * sentinel.  Better to give a spurious wakeup (which should be
                                352                 :          * harmless beyond wasting some cycles) than to lose a wakeup.
                                353                 :          */
                                354             563 :         proc = NULL;
                                355             563 :         SpinLockAcquire(&cv->mutex);
                                356             563 :         if (!proclist_is_empty(&cv->wakeup))
                                357             563 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                358             563 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
                                359             563 :         SpinLockRelease(&cv->mutex);
                                360                 : 
                                361             563 :         if (proc != NULL && proc != MyProc)
                                362             316 :             SetLatch(&proc->procLatch);
                                363                 :     }
 2329 rhaas                     364         2615341 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a