1 /*-------------------------------------------------------------------------
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.
11 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
14 * src/backend/storage/lmgr/condition_variable.c
16 *-------------------------------------------------------------------------
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"
29 /* Initially, we are not prepared to sleep on any condition variable. */
30 static ConditionVariable *cv_sleep_target = NULL;
32 /* Reusable WaitEventSet. */
33 static WaitEventSet *cv_wait_event_set = NULL;
36 * Initialize a condition variable.
39 ConditionVariableInit(ConditionVariable *cv)
41 SpinLockInit(&cv->mutex);
42 proclist_init(&cv->wakeup);
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.
53 ConditionVariablePrepareToSleep(ConditionVariable *cv)
55 int pgprocno = MyProc->pgprocno;
58 * It's not legal to prepare a sleep until the previous sleep has been
59 * completed or canceled.
61 Assert(cv_sleep_target == NULL);
63 /* Record the condition variable on which we will sleep. */
66 /* Create a reusable WaitEventSet. */
67 if (cv_wait_event_set == NULL)
69 cv_wait_event_set = CreateWaitEventSet(TopMemoryContext, 1);
70 AddWaitEventToSet(cv_wait_event_set, WL_LATCH_SET, PGINVALID_SOCKET,
71 &MyProc->procLatch, NULL);
75 * Reset my latch before adding myself to the queue and before entering
76 * the caller's predicate loop.
78 ResetLatch(&MyProc->procLatch);
80 /* Add myself to the wait queue. */
81 SpinLockAcquire(&cv->mutex);
82 if (!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink))
83 proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
84 SpinLockRelease(&cv->mutex);
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:
92 * ConditionVariablePrepareToSleep(cv); [optional]
93 * while (condition for which we are waiting is not true)
94 * ConditionVariableSleep(cv, wait_event_info);
95 * ConditionVariableCancelSleep();
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 *-------------------------------------------------------------------------*/
102 ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
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.
118 if (cv_sleep_target == NULL)
120 ConditionVariablePrepareToSleep(cv);
124 /* Any earlier condition variable sleep must have been canceled. */
125 Assert(cv_sleep_target == cv);
129 CHECK_FOR_INTERRUPTS();
132 * Wait for latch to be set. We don't care about the result because
133 * our contract permits spurious returns.
135 WaitEventSetWait(cv_wait_event_set, -1, &event, 1, wait_event_info);
137 /* Reset latch before testing whether we can return. */
138 ResetLatch(&MyProc->procLatch);
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
150 SpinLockAcquire(&cv->mutex);
151 if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
154 proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
156 SpinLockRelease(&cv->mutex);
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
166 ConditionVariableCancelSleep(void)
168 ConditionVariable *cv = cv_sleep_target;
173 SpinLockAcquire(&cv->mutex);
174 if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
175 proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
176 SpinLockRelease(&cv->mutex);
178 cv_sleep_target = NULL;
182 * Wake up one sleeping process, assuming there is at least one.
184 * The return value indicates whether or not we woke somebody up.
187 ConditionVariableSignal(ConditionVariable *cv)
191 /* Remove the first process from the wakeup queue (if any). */
192 SpinLockAcquire(&cv->mutex);
193 if (!proclist_is_empty(&cv->wakeup))
194 proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
195 SpinLockRelease(&cv->mutex);
197 /* If we found someone sleeping, set their latch to wake them up. */
200 SetLatch(&proc->procLatch);
204 /* No sleeping processes. */
209 * Wake up all sleeping processes.
211 * The return value indicates the number of processes we woke.
214 ConditionVariableBroadcast(ConditionVariable *cv)
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
222 * we don't want to loop holding the mutex.
224 while (ConditionVariableSignal(cv))