LCOV - code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 73 94 77.7 %
Date: 2020-06-01 08:06:25 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * condition_variable.c
       4             :  *    Implementation of condition variables.  Condition variables provide
       5             :  *    a way for one process to wait until a specific condition occurs,
       6             :  *    without needing to know the specific identity of the process for
       7             :  *    which they are waiting.  Waits for condition variables can be
       8             :  *    interrupted, unlike LWLock waits.  Condition variables are safe
       9             :  *    to use within dynamic shared memory segments.
      10             :  *
      11             :  * Portions Copyright (c) 1996-2020, 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             : /* Reusable WaitEventSet. */
      34             : static WaitEventSet *cv_wait_event_set = NULL;
      35             : 
      36             : /*
      37             :  * Initialize a condition variable.
      38             :  */
      39             : void
      40       44904 : ConditionVariableInit(ConditionVariable *cv)
      41             : {
      42       44904 :     SpinLockInit(&cv->mutex);
      43       44904 :     proclist_init(&cv->wakeup);
      44       44904 : }
      45             : 
      46             : /*
      47             :  * Prepare to wait on a given condition variable.
      48             :  *
      49             :  * This can optionally be called before entering a test/sleep loop.
      50             :  * Doing so is more efficient if we'll need to sleep at least once.
      51             :  * However, if the first test of the exit condition is likely to succeed,
      52             :  * it's more efficient to omit the ConditionVariablePrepareToSleep call.
      53             :  * See comments in ConditionVariableSleep for more detail.
      54             :  *
      55             :  * Caution: "before entering the loop" means you *must* test the exit
      56             :  * condition between calling ConditionVariablePrepareToSleep and calling
      57             :  * ConditionVariableSleep.  If that is inconvenient, omit calling
      58             :  * ConditionVariablePrepareToSleep.
      59             :  */
      60             : void
      61        2428 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
      62             : {
      63        2428 :     int         pgprocno = MyProc->pgprocno;
      64             : 
      65             :     /*
      66             :      * If first time through in this process, create a WaitEventSet, which
      67             :      * we'll reuse for all condition variable sleeps.
      68             :      */
      69        2428 :     if (cv_wait_event_set == NULL)
      70             :     {
      71             :         WaitEventSet *new_event_set;
      72             : 
      73         924 :         new_event_set = CreateWaitEventSet(TopMemoryContext, 2);
      74         924 :         AddWaitEventToSet(new_event_set, WL_LATCH_SET, PGINVALID_SOCKET,
      75             :                           MyLatch, NULL);
      76         924 :         AddWaitEventToSet(new_event_set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET,
      77             :                           NULL, NULL);
      78             :         /* Don't set cv_wait_event_set until we have a correct WES. */
      79         924 :         cv_wait_event_set = new_event_set;
      80             :     }
      81             : 
      82             :     /*
      83             :      * If some other sleep is already prepared, cancel it; this is necessary
      84             :      * because we have just one static variable tracking the prepared sleep,
      85             :      * and also only one cvWaitLink in our PGPROC.  It's okay to do this
      86             :      * because whenever control does return to the other test-and-sleep loop,
      87             :      * its ConditionVariableSleep call will just re-establish that sleep as
      88             :      * the prepared one.
      89             :      */
      90        2428 :     if (cv_sleep_target != NULL)
      91           0 :         ConditionVariableCancelSleep();
      92             : 
      93             :     /* Record the condition variable on which we will sleep. */
      94        2428 :     cv_sleep_target = cv;
      95             : 
      96             :     /* Add myself to the wait queue. */
      97        2428 :     SpinLockAcquire(&cv->mutex);
      98        2428 :     proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
      99        2428 :     SpinLockRelease(&cv->mutex);
     100        2428 : }
     101             : 
     102             : /*
     103             :  * Wait for the given condition variable to be signaled.
     104             :  *
     105             :  * This should be called in a predicate loop that tests for a specific exit
     106             :  * condition and otherwise sleeps, like so:
     107             :  *
     108             :  *   ConditionVariablePrepareToSleep(cv);  // optional
     109             :  *   while (condition for which we are waiting is not true)
     110             :  *       ConditionVariableSleep(cv, wait_event_info);
     111             :  *   ConditionVariableCancelSleep();
     112             :  *
     113             :  * wait_event_info should be a value from one of the WaitEventXXX enums
     114             :  * defined in pgstat.h.  This controls the contents of pg_stat_activity's
     115             :  * wait_event_type and wait_event columns while waiting.
     116             :  */
     117             : void
     118        1272 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
     119             : {
     120        1272 :     (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
     121             :                                        wait_event_info);
     122        1272 : }
     123             : 
     124             : /*
     125             :  * Wait for a condition variable to be signaled or a timeout to be reached.
     126             :  *
     127             :  * Returns true when timeout expires, otherwise returns false.
     128             :  *
     129             :  * See ConditionVariableSleep() for general usage.
     130             :  */
     131             : bool
     132        1272 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
     133             :                             uint32 wait_event_info)
     134             : {
     135        1272 :     long        cur_timeout = -1;
     136             :     instr_time  start_time;
     137             :     instr_time  cur_time;
     138             : 
     139             :     /*
     140             :      * If the caller didn't prepare to sleep explicitly, then do so now and
     141             :      * return immediately.  The caller's predicate loop should immediately
     142             :      * call again if its exit condition is not yet met.  This will result in
     143             :      * the exit condition being tested twice before we first sleep.  The extra
     144             :      * test can be prevented by calling ConditionVariablePrepareToSleep(cv)
     145             :      * first.  Whether it's worth doing that depends on whether you expect the
     146             :      * exit condition to be met initially, in which case skipping the prepare
     147             :      * is recommended because it avoids manipulations of the wait list, or not
     148             :      * met initially, in which case preparing first is better because it
     149             :      * avoids one extra test of the exit condition.
     150             :      *
     151             :      * If we are currently prepared to sleep on some other CV, we just cancel
     152             :      * that and prepare this one; see ConditionVariablePrepareToSleep.
     153             :      */
     154        1272 :     if (cv_sleep_target != cv)
     155             :     {
     156          62 :         ConditionVariablePrepareToSleep(cv);
     157          62 :         return false;
     158             :     }
     159             : 
     160             :     /*
     161             :      * Record the current time so that we can calculate the remaining timeout
     162             :      * if we are woken up spuriously.
     163             :      */
     164        1210 :     if (timeout >= 0)
     165             :     {
     166           0 :         INSTR_TIME_SET_CURRENT(start_time);
     167             :         Assert(timeout >= 0 && timeout <= INT_MAX);
     168           0 :         cur_timeout = timeout;
     169             :     }
     170             : 
     171             :     while (true)
     172         536 :     {
     173             :         WaitEvent   event;
     174        1746 :         bool        done = false;
     175             : 
     176             :         /*
     177             :          * Wait for latch to be set.  (If we're awakened for some other
     178             :          * reason, the code below will cope anyway.)
     179             :          */
     180        1746 :         (void) WaitEventSetWait(cv_wait_event_set, cur_timeout, &event, 1,
     181             :                                 wait_event_info);
     182             : 
     183             :         /* Reset latch before examining the state of the wait list. */
     184        1746 :         ResetLatch(MyLatch);
     185             : 
     186        1746 :         CHECK_FOR_INTERRUPTS();
     187             : 
     188             :         /*
     189             :          * If this process has been taken out of the wait list, then we know
     190             :          * that it has been signaled by ConditionVariableSignal (or
     191             :          * ConditionVariableBroadcast), so we should return to the caller. But
     192             :          * that doesn't guarantee that the exit condition is met, only that we
     193             :          * ought to check it.  So we must put the process back into the wait
     194             :          * list, to ensure we don't miss any additional wakeup occurring while
     195             :          * the caller checks its exit condition.  We can take ourselves out of
     196             :          * the wait list only when the caller calls
     197             :          * ConditionVariableCancelSleep.
     198             :          *
     199             :          * If we're still in the wait list, then the latch must have been set
     200             :          * by something other than ConditionVariableSignal; though we don't
     201             :          * guarantee not to return spuriously, we'll avoid this obvious case.
     202             :          */
     203        1746 :         SpinLockAcquire(&cv->mutex);
     204        1746 :         if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
     205             :         {
     206        1210 :             done = true;
     207        1210 :             proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
     208             :         }
     209        1746 :         SpinLockRelease(&cv->mutex);
     210             : 
     211             :         /* We were signaled, so return */
     212        1746 :         if (done)
     213        1210 :             return false;
     214             : 
     215             :         /* If we're not done, update cur_timeout for next iteration */
     216         536 :         if (timeout >= 0)
     217             :         {
     218           0 :             INSTR_TIME_SET_CURRENT(cur_time);
     219           0 :             INSTR_TIME_SUBTRACT(cur_time, start_time);
     220           0 :             cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
     221             : 
     222             :             /* Have we crossed the timeout threshold? */
     223           0 :             if (cur_timeout <= 0)
     224           0 :                 return true;
     225             :         }
     226             :     }
     227             : }
     228             : 
     229             : /*
     230             :  * Cancel any pending sleep operation.
     231             :  *
     232             :  * We just need to remove ourselves from the wait queue of any condition
     233             :  * variable for which we have previously prepared a sleep.
     234             :  *
     235             :  * Do nothing if nothing is pending; this allows this function to be called
     236             :  * during transaction abort to clean up any unfinished CV sleep.
     237             :  */
     238             : void
     239       42314 : ConditionVariableCancelSleep(void)
     240             : {
     241       42314 :     ConditionVariable *cv = cv_sleep_target;
     242       42314 :     bool        signaled = false;
     243             : 
     244       42314 :     if (cv == NULL)
     245       39886 :         return;
     246             : 
     247        2428 :     SpinLockAcquire(&cv->mutex);
     248        2428 :     if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
     249        2428 :         proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
     250             :     else
     251           0 :         signaled = true;
     252        2428 :     SpinLockRelease(&cv->mutex);
     253             : 
     254             :     /*
     255             :      * If we've received a signal, pass it on to another waiting process, if
     256             :      * there is one.  Otherwise a call to ConditionVariableSignal() might get
     257             :      * lost, despite there being another process ready to handle it.
     258             :      */
     259        2428 :     if (signaled)
     260           0 :         ConditionVariableSignal(cv);
     261             : 
     262        2428 :     cv_sleep_target = NULL;
     263             : }
     264             : 
     265             : /*
     266             :  * Wake up the oldest process sleeping on the CV, if there is any.
     267             :  *
     268             :  * Note: it's difficult to tell whether this has any real effect: we know
     269             :  * whether we took an entry off the list, but the entry might only be a
     270             :  * sentinel.  Hence, think twice before proposing that this should return
     271             :  * a flag telling whether it woke somebody.
     272             :  */
     273             : void
     274        1032 : ConditionVariableSignal(ConditionVariable *cv)
     275             : {
     276        1032 :     PGPROC     *proc = NULL;
     277             : 
     278             :     /* Remove the first process from the wakeup queue (if any). */
     279        1032 :     SpinLockAcquire(&cv->mutex);
     280        1032 :     if (!proclist_is_empty(&cv->wakeup))
     281          62 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     282        1032 :     SpinLockRelease(&cv->mutex);
     283             : 
     284             :     /* If we found someone sleeping, set their latch to wake them up. */
     285        1032 :     if (proc != NULL)
     286          62 :         SetLatch(&proc->procLatch);
     287        1032 : }
     288             : 
     289             : /*
     290             :  * Wake up all processes sleeping on the given CV.
     291             :  *
     292             :  * This guarantees to wake all processes that were sleeping on the CV
     293             :  * at time of call, but processes that add themselves to the list mid-call
     294             :  * will typically not get awakened.
     295             :  */
     296             : void
     297        6230 : ConditionVariableBroadcast(ConditionVariable *cv)
     298             : {
     299        6230 :     int         pgprocno = MyProc->pgprocno;
     300        6230 :     PGPROC     *proc = NULL;
     301        6230 :     bool        have_sentinel = false;
     302             : 
     303             :     /*
     304             :      * In some use-cases, it is common for awakened processes to immediately
     305             :      * re-queue themselves.  If we just naively try to reduce the wakeup list
     306             :      * to empty, we'll get into a potentially-indefinite loop against such a
     307             :      * process.  The semantics we really want are just to be sure that we have
     308             :      * wakened all processes that were in the list at entry.  We can use our
     309             :      * own cvWaitLink as a sentinel to detect when we've finished.
     310             :      *
     311             :      * A seeming flaw in this approach is that someone else might signal the
     312             :      * CV and in doing so remove our sentinel entry.  But that's fine: since
     313             :      * CV waiters are always added and removed in order, that must mean that
     314             :      * every previous waiter has been wakened, so we're done.  We'll get an
     315             :      * extra "set" on our latch from the someone else's signal, which is
     316             :      * slightly inefficient but harmless.
     317             :      *
     318             :      * We can't insert our cvWaitLink as a sentinel if it's already in use in
     319             :      * some other proclist.  While that's not expected to be true for typical
     320             :      * uses of this function, we can deal with it by simply canceling any
     321             :      * prepared CV sleep.  The next call to ConditionVariableSleep will take
     322             :      * care of re-establishing the lost state.
     323             :      */
     324        6230 :     if (cv_sleep_target != NULL)
     325           0 :         ConditionVariableCancelSleep();
     326             : 
     327             :     /*
     328             :      * Inspect the state of the queue.  If it's empty, we have nothing to do.
     329             :      * If there's exactly one entry, we need only remove and signal that
     330             :      * entry.  Otherwise, remove the first entry and insert our sentinel.
     331             :      */
     332        6230 :     SpinLockAcquire(&cv->mutex);
     333             :     /* While we're here, let's assert we're not in the list. */
     334             :     Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
     335             : 
     336        6230 :     if (!proclist_is_empty(&cv->wakeup))
     337             :     {
     338         750 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     339         750 :         if (!proclist_is_empty(&cv->wakeup))
     340             :         {
     341           0 :             proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
     342           0 :             have_sentinel = true;
     343             :         }
     344             :     }
     345        6230 :     SpinLockRelease(&cv->mutex);
     346             : 
     347             :     /* Awaken first waiter, if there was one. */
     348        6230 :     if (proc != NULL)
     349         750 :         SetLatch(&proc->procLatch);
     350             : 
     351        6230 :     while (have_sentinel)
     352             :     {
     353             :         /*
     354             :          * Each time through the loop, remove the first wakeup list entry, and
     355             :          * signal it unless it's our sentinel.  Repeat as long as the sentinel
     356             :          * remains in the list.
     357             :          *
     358             :          * Notice that if someone else removes our sentinel, we will waken one
     359             :          * additional process before exiting.  That's intentional, because if
     360             :          * someone else signals the CV, they may be intending to waken some
     361             :          * third process that added itself to the list after we added the
     362             :          * sentinel.  Better to give a spurious wakeup (which should be
     363             :          * harmless beyond wasting some cycles) than to lose a wakeup.
     364             :          */
     365           0 :         proc = NULL;
     366           0 :         SpinLockAcquire(&cv->mutex);
     367           0 :         if (!proclist_is_empty(&cv->wakeup))
     368           0 :             proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     369           0 :         have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
     370           0 :         SpinLockRelease(&cv->mutex);
     371             : 
     372           0 :         if (proc != NULL && proc != MyProc)
     373           0 :             SetLatch(&proc->procLatch);
     374             :     }
     375        6230 : }

Generated by: LCOV version 1.13