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