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 15:15:32 Functions: 100.0 % 7 7 7
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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
      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                 : {
      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                 :      */
      70            4215 :     if (cv_sleep_target != NULL)
      71 UBC           0 :         ConditionVariableCancelSleep();
      72                 : 
      73                 :     /* Record the condition variable on which we will sleep. */
      74 CBC        4215 :     cv_sleep_target = cv;
      75                 : 
      76                 :     /* Add myself to the wait queue. */
      77            4215 :     SpinLockAcquire(&cv->mutex);
      78            4215 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
      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                 : {
     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                 :      */
     135            2555 :     if (cv_sleep_target != cv)
     136                 :     {
     137             153 :         ConditionVariablePrepareToSleep(cv);
     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;
     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)
     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                 :          */
     163            3029 :         (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
     164                 : 
     165                 :         /* Reset latch before examining the state of the wait list. */
     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                 :          */
     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                 :          */
     196            3029 :         CHECK_FOR_INTERRUPTS();
     197            3029 :         if (cv != cv_sleep_target)
     198              38 :             done = true;
     199                 : 
     200                 :         /* We were signaled, so return */
     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
     228           48766 : ConditionVariableCancelSleep(void)
     229                 : {
     230           48766 :     ConditionVariable *cv = cv_sleep_target;
     231           48766 :     bool        signaled = false;
     232                 : 
     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
     240              59 :         signaled = true;
     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                 :      */
     248            4215 :     if (signaled)
     249              59 :         ConditionVariableSignal(cv);
     250                 : 
     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                 : {
     265             845 :     PGPROC     *proc = NULL;
     266                 : 
     267                 :     /* Remove the first process from the wakeup queue (if any). */
     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                 : {
     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                 :      */
     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                 :      */
     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                 :     }
     364         2615341 : }
        

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