LCOV - code coverage report
Current view: top level - src/backend/storage/lmgr - condition_variable.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 19 56 33.9 %
Date: 2017-09-29 13:40:31 Functions: 4 6 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * condition_variable.c
       4             :  *    Implementation of condition variables.  Condition variables provide
       5             :  *    a way for one process to wait until a specific condition occurs,
       6             :  *    without needing to know the specific identity of the process for
       7             :  *    which they are waiting.  Waits for condition variables can be
       8             :  *    interrupted, unlike LWLock waits.  Condition variables are safe
       9             :  *    to use within dynamic shared memory segments.
      10             :  *
      11             :  * Portions Copyright (c) 1996-2017, 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 "storage/condition_variable.h"
      23             : #include "storage/ipc.h"
      24             : #include "storage/proc.h"
      25             : #include "storage/proclist.h"
      26             : #include "storage/spin.h"
      27             : #include "utils/memutils.h"
      28             : 
      29             : /* Initially, we are not prepared to sleep on any condition variable. */
      30             : static ConditionVariable *cv_sleep_target = NULL;
      31             : 
      32             : /* Reusable WaitEventSet. */
      33             : static WaitEventSet *cv_wait_event_set = NULL;
      34             : 
      35             : /*
      36             :  * Initialize a condition variable.
      37             :  */
      38             : void
      39         107 : ConditionVariableInit(ConditionVariable *cv)
      40             : {
      41         107 :     SpinLockInit(&cv->mutex);
      42         107 :     proclist_init(&cv->wakeup);
      43         107 : }
      44             : 
      45             : /*
      46             :  * Prepare to wait on a given condition variable.  This can optionally be
      47             :  * called before entering a test/sleep loop.  Alternatively, the call to
      48             :  * ConditionVariablePrepareToSleep can be omitted.  The only advantage of
      49             :  * calling ConditionVariablePrepareToSleep is that it avoids an initial
      50             :  * double-test of the user's predicate in the case that we need to wait.
      51             :  */
      52             : void
      53           0 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
      54             : {
      55           0 :     int         pgprocno = MyProc->pgprocno;
      56             : 
      57             :     /*
      58             :      * It's not legal to prepare a sleep until the previous sleep has been
      59             :      * completed or canceled.
      60             :      */
      61           0 :     Assert(cv_sleep_target == NULL);
      62             : 
      63             :     /* Record the condition variable on which we will sleep. */
      64           0 :     cv_sleep_target = cv;
      65             : 
      66             :     /* Create a reusable WaitEventSet. */
      67           0 :     if (cv_wait_event_set == NULL)
      68             :     {
      69           0 :         cv_wait_event_set = CreateWaitEventSet(TopMemoryContext, 1);
      70           0 :         AddWaitEventToSet(cv_wait_event_set, WL_LATCH_SET, PGINVALID_SOCKET,
      71             :                           MyLatch, NULL);
      72             :     }
      73             : 
      74             :     /*
      75             :      * Reset my latch before adding myself to the queue and before entering
      76             :      * the caller's predicate loop.
      77             :      */
      78           0 :     ResetLatch(MyLatch);
      79             : 
      80             :     /* Add myself to the wait queue. */
      81           0 :     SpinLockAcquire(&cv->mutex);
      82           0 :     if (!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink))
      83           0 :         proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
      84           0 :     SpinLockRelease(&cv->mutex);
      85           0 : }
      86             : 
      87             : /*--------------------------------------------------------------------------
      88             :  * Wait for the given condition variable to be signaled.  This should be
      89             :  * called in a predicate loop that tests for a specific exit condition and
      90             :  * otherwise sleeps, like so:
      91             :  *
      92             :  *   ConditionVariablePrepareToSleep(cv); [optional]
      93             :  *   while (condition for which we are waiting is not true)
      94             :  *       ConditionVariableSleep(cv, wait_event_info);
      95             :  *   ConditionVariableCancelSleep();
      96             :  *
      97             :  * Supply a value from one of the WaitEventXXX enums defined in pgstat.h to
      98             :  * control the contents of pg_stat_activity's wait_event_type and wait_event
      99             :  * columns while waiting.
     100             :  *-------------------------------------------------------------------------*/
     101             : void
     102           0 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
     103             : {
     104             :     WaitEvent   event;
     105           0 :     bool        done = false;
     106             : 
     107             :     /*
     108             :      * If the caller didn't prepare to sleep explicitly, then do so now and
     109             :      * return immediately.  The caller's predicate loop should immediately
     110             :      * call again if its exit condition is not yet met.  This initial spurious
     111             :      * return can be avoided by calling ConditionVariablePrepareToSleep(cv)
     112             :      * first.  Whether it's worth doing that depends on whether you expect the
     113             :      * condition to be met initially, in which case skipping the prepare
     114             :      * allows you to skip manipulation of the wait list, or not met initially,
     115             :      * in which case preparing first allows you to skip a spurious test of the
     116             :      * caller's exit condition.
     117             :      */
     118           0 :     if (cv_sleep_target == NULL)
     119             :     {
     120           0 :         ConditionVariablePrepareToSleep(cv);
     121           0 :         return;
     122             :     }
     123             : 
     124             :     /* Any earlier condition variable sleep must have been canceled. */
     125           0 :     Assert(cv_sleep_target == cv);
     126             : 
     127           0 :     while (!done)
     128             :     {
     129           0 :         CHECK_FOR_INTERRUPTS();
     130             : 
     131             :         /*
     132             :          * Wait for latch to be set.  We don't care about the result because
     133             :          * our contract permits spurious returns.
     134             :          */
     135           0 :         WaitEventSetWait(cv_wait_event_set, -1, &event, 1, wait_event_info);
     136             : 
     137             :         /* Reset latch before testing whether we can return. */
     138           0 :         ResetLatch(MyLatch);
     139             : 
     140             :         /*
     141             :          * If this process has been taken out of the wait list, then we know
     142             :          * that is has been signaled by ConditionVariableSignal.  We put it
     143             :          * back into the wait list, so we don't miss any further signals while
     144             :          * the caller's loop checks its condition.  If it hasn't been taken
     145             :          * out of the wait list, then the latch must have been set by
     146             :          * something other than ConditionVariableSignal; though we don't
     147             :          * guarantee not to return spuriously, we'll avoid these obvious
     148             :          * cases.
     149             :          */
     150           0 :         SpinLockAcquire(&cv->mutex);
     151           0 :         if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
     152             :         {
     153           0 :             done = true;
     154           0 :             proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
     155             :         }
     156           0 :         SpinLockRelease(&cv->mutex);
     157             :     }
     158             : }
     159             : 
     160             : /*
     161             :  * Cancel any pending sleep operation.  We just need to remove ourselves
     162             :  * from the wait queue of any condition variable for which we have previously
     163             :  * prepared a sleep.
     164             :  */
     165             : void
     166        3932 : ConditionVariableCancelSleep(void)
     167             : {
     168        3932 :     ConditionVariable *cv = cv_sleep_target;
     169             : 
     170        3932 :     if (cv == NULL)
     171        7864 :         return;
     172             : 
     173           0 :     SpinLockAcquire(&cv->mutex);
     174           0 :     if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
     175           0 :         proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
     176           0 :     SpinLockRelease(&cv->mutex);
     177             : 
     178           0 :     cv_sleep_target = NULL;
     179             : }
     180             : 
     181             : /*
     182             :  * Wake up one sleeping process, assuming there is at least one.
     183             :  *
     184             :  * The return value indicates whether or not we woke somebody up.
     185             :  */
     186             : bool
     187         228 : ConditionVariableSignal(ConditionVariable *cv)
     188             : {
     189         228 :     PGPROC     *proc = NULL;
     190             : 
     191             :     /* Remove the first process from the wakeup queue (if any). */
     192         228 :     SpinLockAcquire(&cv->mutex);
     193         228 :     if (!proclist_is_empty(&cv->wakeup))
     194           0 :         proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
     195         228 :     SpinLockRelease(&cv->mutex);
     196             : 
     197             :     /* If we found someone sleeping, set their latch to wake them up. */
     198         228 :     if (proc != NULL)
     199             :     {
     200           0 :         SetLatch(&proc->procLatch);
     201           0 :         return true;
     202             :     }
     203             : 
     204             :     /* No sleeping processes. */
     205         228 :     return false;
     206             : }
     207             : 
     208             : /*
     209             :  * Wake up all sleeping processes.
     210             :  *
     211             :  * The return value indicates the number of processes we woke.
     212             :  */
     213             : int
     214          20 : ConditionVariableBroadcast(ConditionVariable *cv)
     215             : {
     216          20 :     int         nwoken = 0;
     217             : 
     218             :     /*
     219             :      * Let's just do this the dumbest way possible.  We could try to dequeue
     220             :      * all the sleepers at once to save spinlock cycles, but it's a bit hard
     221             :      * to get that right in the face of possible sleep cancelations, and we
     222             :      * don't want to loop holding the mutex.
     223             :      */
     224          40 :     while (ConditionVariableSignal(cv))
     225           0 :         ++nwoken;
     226             : 
     227          20 :     return nwoken;
     228             : }

Generated by: LCOV version 1.11