LCOV - differential code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 98.9 % 94 93 1 6 87 6
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 7 7 4 3
Baseline: 16@8cea358b128 Branches: 90.0 % 60 54 6 4 50
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed [..60] days: 100.0 % 6 6 6
(240..) days: 98.9 % 88 87 1 87
Function coverage date bins:
(240..) days: 100.0 % 7 7 4 3
Branch coverage date bins:
[..60] days: 100.0 % 4 4 4
(240..) days: 89.3 % 56 50 6 50

 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-2024, 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/proc.h"
                                 25                 :                : #include "storage/proclist.h"
                                 26                 :                : #include "storage/spin.h"
                                 27                 :                : 
                                 28                 :                : /* Initially, we are not prepared to sleep on any condition variable. */
                                 29                 :                : static ConditionVariable *cv_sleep_target = NULL;
                                 30                 :                : 
                                 31                 :                : /*
                                 32                 :                :  * Initialize a condition variable.
                                 33                 :                :  */
                                 34                 :                : void
 2700 rhaas@postgresql.org       35                 :CBC     8382932 : ConditionVariableInit(ConditionVariable *cv)
                                 36                 :                : {
                                 37                 :        8382932 :     SpinLockInit(&cv->mutex);
                                 38                 :        8382932 :     proclist_init(&cv->wakeup);
                                 39                 :        8382932 : }
                                 40                 :                : 
                                 41                 :                : /*
                                 42                 :                :  * Prepare to wait on a given condition variable.
                                 43                 :                :  *
                                 44                 :                :  * This can optionally be called before entering a test/sleep loop.
                                 45                 :                :  * Doing so is more efficient if we'll need to sleep at least once.
                                 46                 :                :  * However, if the first test of the exit condition is likely to succeed,
                                 47                 :                :  * it's more efficient to omit the ConditionVariablePrepareToSleep call.
                                 48                 :                :  * See comments in ConditionVariableSleep for more detail.
                                 49                 :                :  *
                                 50                 :                :  * Caution: "before entering the loop" means you *must* test the exit
                                 51                 :                :  * condition between calling ConditionVariablePrepareToSleep and calling
                                 52                 :                :  * ConditionVariableSleep.  If that is inconvenient, omit calling
                                 53                 :                :  * ConditionVariablePrepareToSleep.
                                 54                 :                :  */
                                 55                 :                : void
                                 56                 :          73594 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
                                 57                 :                : {
   52 heikki.linnakangas@i       58                 :GNC       73594 :     int         pgprocno = MyProcNumber;
                                 59                 :                : 
                                 60                 :                :     /*
                                 61                 :                :      * If some other sleep is already prepared, cancel it; this is necessary
                                 62                 :                :      * because we have just one static variable tracking the prepared sleep,
                                 63                 :                :      * and also only one cvWaitLink in our PGPROC.  It's okay to do this
                                 64                 :                :      * because whenever control does return to the other test-and-sleep loop,
                                 65                 :                :      * its ConditionVariableSleep call will just re-establish that sleep as
                                 66                 :                :      * the prepared one.
                                 67                 :                :      */
 2287 tgl@sss.pgh.pa.us          68         [ -  + ]:CBC       73594 :     if (cv_sleep_target != NULL)
 2287 tgl@sss.pgh.pa.us          69                 :UBC           0 :         ConditionVariableCancelSleep();
                                 70                 :                : 
                                 71                 :                :     /* Record the condition variable on which we will sleep. */
 2700 rhaas@postgresql.org       72                 :CBC       73594 :     cv_sleep_target = cv;
                                 73                 :                : 
                                 74                 :                :     /* Add myself to the wait queue. */
                                 75         [ +  + ]:          73594 :     SpinLockAcquire(&cv->mutex);
 2288 tgl@sss.pgh.pa.us          76                 :          73594 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
 2700 rhaas@postgresql.org       77                 :          73594 :     SpinLockRelease(&cv->mutex);
                                 78                 :          73594 : }
                                 79                 :                : 
                                 80                 :                : /*
                                 81                 :                :  * Wait for the given condition variable to be signaled.
                                 82                 :                :  *
                                 83                 :                :  * This should be called in a predicate loop that tests for a specific exit
                                 84                 :                :  * condition and otherwise sleeps, like so:
                                 85                 :                :  *
                                 86                 :                :  *   ConditionVariablePrepareToSleep(cv);  // optional
                                 87                 :                :  *   while (condition for which we are waiting is not true)
                                 88                 :                :  *       ConditionVariableSleep(cv, wait_event_info);
                                 89                 :                :  *   ConditionVariableCancelSleep();
                                 90                 :                :  *
                                 91                 :                :  * wait_event_info should be a value from one of the WaitEventXXX enums
                                 92                 :                :  * defined in pgstat.h.  This controls the contents of pg_stat_activity's
                                 93                 :                :  * wait_event_type and wait_event columns while waiting.
                                 94                 :                :  */
                                 95                 :                : void
                                 96                 :           5413 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
                                 97                 :                : {
 1737 tmunro@postgresql.or       98                 :           5413 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
                                 99                 :                :                                        wait_event_info);
                                100                 :           5413 : }
                                101                 :                : 
                                102                 :                : /*
                                103                 :                :  * Wait for a condition variable to be signaled or a timeout to be reached.
                                104                 :                :  *
                                105                 :                :  * The "timeout" is given in milliseconds.
                                106                 :                :  *
                                107                 :                :  * Returns true when timeout expires, otherwise returns false.
                                108                 :                :  *
                                109                 :                :  * See ConditionVariableSleep() for general usage.
                                110                 :                :  */
                                111                 :                : bool
                                112                 :           5794 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
                                113                 :                :                             uint32 wait_event_info)
                                114                 :                : {
                                115                 :           5794 :     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                 :                :      */
 2287 tgl@sss.pgh.pa.us         135         [ +  + ]:           5794 :     if (cv_sleep_target != cv)
                                136                 :                :     {
 2700 rhaas@postgresql.org      137                 :            310 :         ConditionVariablePrepareToSleep(cv);
 1737 tmunro@postgresql.or      138                 :            310 :         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         [ +  + ]:           5484 :     if (timeout >= 0)
                                146                 :                :     {
                                147                 :            190 :         INSTR_TIME_SET_CURRENT(start_time);
                                148   [ +  -  -  + ]:            190 :         Assert(timeout >= 0 && timeout <= INT_MAX);
                                149                 :            190 :         cur_timeout = timeout;
 1354                           150                 :            190 :         wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
                                151                 :                :     }
                                152                 :                :     else
                                153                 :           5294 :         wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
                                154                 :                : 
                                155                 :                :     while (true)
 1737                           156                 :            812 :     {
                                157                 :           6296 :         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                 :                :          */
 1354                           163                 :           6296 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
                                164                 :                : 
                                165                 :                :         /* Reset latch before examining the state of the wait list. */
 2504 andres@anarazel.de        166                 :           6290 :         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                 :                :          */
 2700 rhaas@postgresql.org      183         [ +  + ]:           6290 :         SpinLockAcquire(&cv->mutex);
   52 heikki.linnakangas@i      184         [ +  + ]:GNC        6290 :         if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
                                185                 :                :         {
 2700 rhaas@postgresql.org      186                 :CBC        5409 :             done = true;
   52 heikki.linnakangas@i      187                 :GNC        5409 :             proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
                                188                 :                :         }
 2700 rhaas@postgresql.org      189                 :CBC        6290 :         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                 :                :          */
 1140 tmunro@postgresql.or      196         [ +  + ]:           6290 :         CHECK_FOR_INTERRUPTS();
                                197         [ +  + ]:           6290 :         if (cv != cv_sleep_target)
                                198                 :             72 :             done = true;
                                199                 :                : 
                                200                 :                :         /* We were signaled, so return */
 1737                           201         [ +  + ]:           6290 :         if (done)
                                202                 :           5476 :             return false;
                                203                 :                : 
                                204                 :                :         /* If we're not done, update cur_timeout for next iteration */
                                205         [ +  + ]:            814 :         if (timeout >= 0)
                                206                 :                :         {
                                207                 :             41 :             INSTR_TIME_SET_CURRENT(cur_time);
                                208                 :             41 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
                                209                 :             41 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
                                210                 :                : 
                                211                 :                :             /* Have we crossed the timeout threshold? */
                                212         [ +  + ]:             41 :             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                 :                :  * Return true if we've been signaled.
                                228                 :                :  */
                                229                 :                : bool
 2700 rhaas@postgresql.org      230                 :         128441 : ConditionVariableCancelSleep(void)
                                231                 :                : {
                                232                 :         128441 :     ConditionVariable *cv = cv_sleep_target;
 1737 tmunro@postgresql.or      233                 :         128441 :     bool        signaled = false;
                                234                 :                : 
 2700 rhaas@postgresql.org      235         [ +  + ]:         128441 :     if (cv == NULL)
  243 tmunro@postgresql.or      236                 :          54902 :         return false;
                                237                 :                : 
 2700 rhaas@postgresql.org      238         [ +  + ]:          73539 :     SpinLockAcquire(&cv->mutex);
   52 heikki.linnakangas@i      239         [ +  + ]:GNC       73539 :     if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
                                240                 :          50485 :         proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
                                241                 :                :     else
 1737 tmunro@postgresql.or      242                 :CBC       23054 :         signaled = true;
 2700 rhaas@postgresql.org      243                 :          73539 :     SpinLockRelease(&cv->mutex);
                                244                 :                : 
                                245                 :          73539 :     cv_sleep_target = NULL;
                                246                 :                : 
  243 tmunro@postgresql.or      247                 :          73539 :     return signaled;
                                248                 :                : }
                                249                 :                : 
                                250                 :                : /*
                                251                 :                :  * Wake up the oldest process sleeping on the CV, if there is any.
                                252                 :                :  *
                                253                 :                :  * Note: it's difficult to tell whether this has any real effect: we know
                                254                 :                :  * whether we took an entry off the list, but the entry might only be a
                                255                 :                :  * sentinel.  Hence, think twice before proposing that this should return
                                256                 :                :  * a flag telling whether it woke somebody.
                                257                 :                :  */
                                258                 :                : void
 2700 rhaas@postgresql.org      259                 :            811 : ConditionVariableSignal(ConditionVariable *cv)
                                260                 :                : {
 2524 bruce@momjian.us          261                 :            811 :     PGPROC     *proc = NULL;
                                262                 :                : 
                                263                 :                :     /* Remove the first process from the wakeup queue (if any). */
 2700 rhaas@postgresql.org      264         [ +  + ]:            811 :     SpinLockAcquire(&cv->mutex);
                                265         [ +  + ]:            811 :     if (!proclist_is_empty(&cv->wakeup))
                                266                 :            105 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                267                 :            811 :     SpinLockRelease(&cv->mutex);
                                268                 :                : 
                                269                 :                :     /* If we found someone sleeping, set their latch to wake them up. */
                                270         [ +  + ]:            811 :     if (proc != NULL)
                                271                 :            105 :         SetLatch(&proc->procLatch);
                                272                 :            811 : }
                                273                 :                : 
                                274                 :                : /*
                                275                 :                :  * Wake up all processes sleeping on the given CV.
                                276                 :                :  *
                                277                 :                :  * This guarantees to wake all processes that were sleeping on the CV
                                278                 :                :  * at time of call, but processes that add themselves to the list mid-call
                                279                 :                :  * will typically not get awakened.
                                280                 :                :  */
                                281                 :                : void
                                282                 :        4897077 : ConditionVariableBroadcast(ConditionVariable *cv)
                                283                 :                : {
   52 heikki.linnakangas@i      284                 :GNC     4897077 :     int         pgprocno = MyProcNumber;
 2291 tgl@sss.pgh.pa.us         285                 :CBC     4897077 :     PGPROC     *proc = NULL;
                                286                 :        4897077 :     bool        have_sentinel = false;
                                287                 :                : 
                                288                 :                :     /*
                                289                 :                :      * In some use-cases, it is common for awakened processes to immediately
                                290                 :                :      * re-queue themselves.  If we just naively try to reduce the wakeup list
                                291                 :                :      * to empty, we'll get into a potentially-indefinite loop against such a
                                292                 :                :      * process.  The semantics we really want are just to be sure that we have
                                293                 :                :      * wakened all processes that were in the list at entry.  We can use our
                                294                 :                :      * own cvWaitLink as a sentinel to detect when we've finished.
                                295                 :                :      *
                                296                 :                :      * A seeming flaw in this approach is that someone else might signal the
                                297                 :                :      * CV and in doing so remove our sentinel entry.  But that's fine: since
                                298                 :                :      * CV waiters are always added and removed in order, that must mean that
                                299                 :                :      * every previous waiter has been wakened, so we're done.  We'll get an
                                300                 :                :      * extra "set" on our latch from the someone else's signal, which is
                                301                 :                :      * slightly inefficient but harmless.
                                302                 :                :      *
                                303                 :                :      * We can't insert our cvWaitLink as a sentinel if it's already in use in
                                304                 :                :      * some other proclist.  While that's not expected to be true for typical
                                305                 :                :      * uses of this function, we can deal with it by simply canceling any
                                306                 :                :      * prepared CV sleep.  The next call to ConditionVariableSleep will take
                                307                 :                :      * care of re-establishing the lost state.
                                308                 :                :      */
 2287                           309         [ +  + ]:        4897077 :     if (cv_sleep_target != NULL)
                                310                 :             86 :         ConditionVariableCancelSleep();
                                311                 :                : 
                                312                 :                :     /*
                                313                 :                :      * Inspect the state of the queue.  If it's empty, we have nothing to do.
                                314                 :                :      * If there's exactly one entry, we need only remove and signal that
                                315                 :                :      * entry.  Otherwise, remove the first entry and insert our sentinel.
                                316                 :                :      */
 2291                           317         [ +  + ]:        4897077 :     SpinLockAcquire(&cv->mutex);
                                318                 :                :     /* While we're here, let's assert we're not in the list. */
                                319         [ -  + ]:        4897077 :     Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
                                320                 :                : 
                                321         [ +  + ]:        4897077 :     if (!proclist_is_empty(&cv->wakeup))
                                322                 :                :     {
                                323                 :          27451 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                324         [ +  + ]:          27451 :         if (!proclist_is_empty(&cv->wakeup))
                                325                 :                :         {
                                326                 :            806 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
                                327                 :            806 :             have_sentinel = true;
                                328                 :                :         }
                                329                 :                :     }
                                330                 :        4897077 :     SpinLockRelease(&cv->mutex);
                                331                 :                : 
                                332                 :                :     /* Awaken first waiter, if there was one. */
                                333         [ +  + ]:        4897077 :     if (proc != NULL)
                                334                 :          27451 :         SetLatch(&proc->procLatch);
                                335                 :                : 
                                336         [ +  + ]:        4898826 :     while (have_sentinel)
                                337                 :                :     {
                                338                 :                :         /*
                                339                 :                :          * Each time through the loop, remove the first wakeup list entry, and
                                340                 :                :          * signal it unless it's our sentinel.  Repeat as long as the sentinel
                                341                 :                :          * remains in the list.
                                342                 :                :          *
                                343                 :                :          * Notice that if someone else removes our sentinel, we will waken one
                                344                 :                :          * additional process before exiting.  That's intentional, because if
                                345                 :                :          * someone else signals the CV, they may be intending to waken some
                                346                 :                :          * third process that added itself to the list after we added the
                                347                 :                :          * sentinel.  Better to give a spurious wakeup (which should be
                                348                 :                :          * harmless beyond wasting some cycles) than to lose a wakeup.
                                349                 :                :          */
                                350                 :           1749 :         proc = NULL;
                                351         [ +  + ]:           1749 :         SpinLockAcquire(&cv->mutex);
                                352         [ +  - ]:           1749 :         if (!proclist_is_empty(&cv->wakeup))
                                353                 :           1749 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
                                354                 :           1749 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
                                355                 :           1749 :         SpinLockRelease(&cv->mutex);
                                356                 :                : 
                                357   [ +  -  +  + ]:           1749 :         if (proc != NULL && proc != MyProc)
                                358                 :            943 :             SetLatch(&proc->procLatch);
                                359                 :                :     }
 2700 rhaas@postgresql.org      360                 :        4897077 : }
        

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