Age Owner Branch data 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-2025, 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 <limits.h>
22 : :
23 : : #include "miscadmin.h"
24 : : #include "portability/instr_time.h"
25 : : #include "storage/condition_variable.h"
26 : : #include "storage/proc.h"
27 : : #include "storage/proclist.h"
28 : : #include "storage/spin.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
3312 rhaas@postgresql.org 37 :CBC 16666868 : ConditionVariableInit(ConditionVariable *cv)
38 : : {
39 : 16666868 : SpinLockInit(&cv->mutex);
40 : 16666868 : proclist_init(&cv->wakeup);
41 : 16666868 : }
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 : 418668 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
59 : : {
664 heikki.linnakangas@i 60 : 418668 : int pgprocno = MyProcNumber;
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 : : */
2899 tgl@sss.pgh.pa.us 70 [ + + ]: 418668 : if (cv_sleep_target != NULL)
71 : 2529 : ConditionVariableCancelSleep();
72 : :
73 : : /* Record the condition variable on which we will sleep. */
3312 rhaas@postgresql.org 74 : 418668 : cv_sleep_target = cv;
75 : :
76 : : /* Add myself to the wait queue. */
77 [ + + ]: 418668 : SpinLockAcquire(&cv->mutex);
2900 tgl@sss.pgh.pa.us 78 : 418668 : proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
3312 rhaas@postgresql.org 79 : 418668 : SpinLockRelease(&cv->mutex);
80 : 418668 : }
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 : 313311 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
99 : : {
2349 tmunro@postgresql.or 100 : 313311 : (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
101 : : wait_event_info);
102 : 313310 : }
103 : :
104 : : /*
105 : : * Wait for a condition variable to be signaled or a timeout to be reached.
106 : : *
107 : : * The "timeout" is given in milliseconds.
108 : : *
109 : : * Returns true when timeout expires, otherwise returns false.
110 : : *
111 : : * See ConditionVariableSleep() for general usage.
112 : : */
113 : : bool
114 : 313814 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
115 : : uint32 wait_event_info)
116 : : {
117 : 313814 : long cur_timeout = -1;
118 : : instr_time start_time;
119 : : instr_time cur_time;
120 : : int wait_events;
121 : :
122 : : /*
123 : : * If the caller didn't prepare to sleep explicitly, then do so now and
124 : : * return immediately. The caller's predicate loop should immediately
125 : : * call again if its exit condition is not yet met. This will result in
126 : : * the exit condition being tested twice before we first sleep. The extra
127 : : * test can be prevented by calling ConditionVariablePrepareToSleep(cv)
128 : : * first. Whether it's worth doing that depends on whether you expect the
129 : : * exit condition to be met initially, in which case skipping the prepare
130 : : * is recommended because it avoids manipulations of the wait list, or not
131 : : * met initially, in which case preparing first is better because it
132 : : * avoids one extra test of the exit condition.
133 : : *
134 : : * If we are currently prepared to sleep on some other CV, we just cancel
135 : : * that and prepare this one; see ConditionVariablePrepareToSleep.
136 : : */
2899 tgl@sss.pgh.pa.us 137 [ + + ]: 313814 : if (cv_sleep_target != cv)
138 : : {
3312 rhaas@postgresql.org 139 : 337 : ConditionVariablePrepareToSleep(cv);
2349 tmunro@postgresql.or 140 : 337 : return false;
141 : : }
142 : :
143 : : /*
144 : : * Record the current time so that we can calculate the remaining timeout
145 : : * if we are woken up spuriously.
146 : : */
147 [ + + ]: 313477 : if (timeout >= 0)
148 : : {
149 : 252 : INSTR_TIME_SET_CURRENT(start_time);
150 [ + - - + ]: 252 : Assert(timeout >= 0 && timeout <= INT_MAX);
151 : 252 : cur_timeout = timeout;
1966 152 : 252 : wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
153 : : }
154 : : else
155 : 313225 : wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
156 : :
157 : : while (true)
2349 158 : 3244 : {
159 : 316721 : bool done = false;
160 : :
161 : : /*
162 : : * Wait for latch to be set. (If we're awakened for some other
163 : : * reason, the code below will cope anyway.)
164 : : */
1966 165 : 316721 : (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
166 : :
167 : : /* Reset latch before examining the state of the wait list. */
3116 andres@anarazel.de 168 : 316721 : ResetLatch(MyLatch);
169 : :
170 : : /*
171 : : * If this process has been taken out of the wait list, then we know
172 : : * that it has been signaled by ConditionVariableSignal (or
173 : : * ConditionVariableBroadcast), so we should return to the caller. But
174 : : * that doesn't guarantee that the exit condition is met, only that we
175 : : * ought to check it. So we must put the process back into the wait
176 : : * list, to ensure we don't miss any additional wakeup occurring while
177 : : * the caller checks its exit condition. We can take ourselves out of
178 : : * the wait list only when the caller calls
179 : : * ConditionVariableCancelSleep.
180 : : *
181 : : * If we're still in the wait list, then the latch must have been set
182 : : * by something other than ConditionVariableSignal; though we don't
183 : : * guarantee not to return spuriously, we'll avoid this obvious case.
184 : : */
3312 rhaas@postgresql.org 185 [ + + ]: 316721 : SpinLockAcquire(&cv->mutex);
664 heikki.linnakangas@i 186 [ + + ]: 316721 : if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
187 : : {
3312 rhaas@postgresql.org 188 : 313396 : done = true;
664 heikki.linnakangas@i 189 : 313396 : proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
190 : : }
3312 rhaas@postgresql.org 191 : 316721 : SpinLockRelease(&cv->mutex);
192 : :
193 : : /*
194 : : * Check for interrupts, and return spuriously if that caused the
195 : : * current sleep target to change (meaning that interrupt handler code
196 : : * waited for a different condition variable).
197 : : */
1752 tmunro@postgresql.or 198 [ + + ]: 316721 : CHECK_FOR_INTERRUPTS();
199 [ + + ]: 316720 : if (cv != cv_sleep_target)
200 : 81 : done = true;
201 : :
202 : : /* We were signaled, so return */
2349 203 [ + + ]: 316720 : if (done)
204 : 313473 : return false;
205 : :
206 : : /* If we're not done, update cur_timeout for next iteration */
207 [ + + ]: 3247 : if (timeout >= 0)
208 : : {
209 : 31 : INSTR_TIME_SET_CURRENT(cur_time);
210 : 31 : INSTR_TIME_SUBTRACT(cur_time, start_time);
211 : 31 : cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
212 : :
213 : : /* Have we crossed the timeout threshold? */
214 [ + + ]: 31 : if (cur_timeout <= 0)
215 : 3 : return true;
216 : : }
217 : : }
218 : : }
219 : :
220 : : /*
221 : : * Cancel any pending sleep operation.
222 : : *
223 : : * We just need to remove ourselves from the wait queue of any condition
224 : : * variable for which we have previously prepared a sleep.
225 : : *
226 : : * Do nothing if nothing is pending; this allows this function to be called
227 : : * during transaction abort to clean up any unfinished CV sleep.
228 : : *
229 : : * Return true if we've been signaled.
230 : : */
231 : : bool
3312 rhaas@postgresql.org 232 : 485150 : ConditionVariableCancelSleep(void)
233 : : {
234 : 485150 : ConditionVariable *cv = cv_sleep_target;
2349 tmunro@postgresql.or 235 : 485150 : bool signaled = false;
236 : :
3312 rhaas@postgresql.org 237 [ + + ]: 485150 : if (cv == NULL)
855 tmunro@postgresql.or 238 : 66482 : return false;
239 : :
3312 rhaas@postgresql.org 240 [ + + ]: 418668 : SpinLockAcquire(&cv->mutex);
664 heikki.linnakangas@i 241 [ + + ]: 418668 : if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
242 : 394820 : proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
243 : : else
2349 tmunro@postgresql.or 244 : 23848 : signaled = true;
3312 rhaas@postgresql.org 245 : 418668 : SpinLockRelease(&cv->mutex);
246 : :
247 : 418668 : cv_sleep_target = NULL;
248 : :
855 tmunro@postgresql.or 249 : 418668 : return signaled;
250 : : }
251 : :
252 : : /*
253 : : * Wake up the oldest process sleeping on the CV, if there is any.
254 : : *
255 : : * Note: it's difficult to tell whether this has any real effect: we know
256 : : * whether we took an entry off the list, but the entry might only be a
257 : : * sentinel. Hence, think twice before proposing that this should return
258 : : * a flag telling whether it woke somebody.
259 : : */
260 : : void
3312 rhaas@postgresql.org 261 : 832 : ConditionVariableSignal(ConditionVariable *cv)
262 : : {
3136 bruce@momjian.us 263 : 832 : PGPROC *proc = NULL;
264 : :
265 : : /* Remove the first process from the wakeup queue (if any). */
3312 rhaas@postgresql.org 266 [ - + ]: 832 : SpinLockAcquire(&cv->mutex);
267 [ + + ]: 832 : if (!proclist_is_empty(&cv->wakeup))
268 : 75 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
269 : 832 : SpinLockRelease(&cv->mutex);
270 : :
271 : : /* If we found someone sleeping, set their latch to wake them up. */
272 [ + + ]: 832 : if (proc != NULL)
273 : 75 : SetLatch(&proc->procLatch);
274 : 832 : }
275 : :
276 : : /*
277 : : * Wake up all processes sleeping on the given CV.
278 : : *
279 : : * This guarantees to wake all processes that were sleeping on the CV
280 : : * at time of call, but processes that add themselves to the list mid-call
281 : : * will typically not get awakened.
282 : : */
283 : : void
284 : 6505918 : ConditionVariableBroadcast(ConditionVariable *cv)
285 : : {
664 heikki.linnakangas@i 286 : 6505918 : int pgprocno = MyProcNumber;
2903 tgl@sss.pgh.pa.us 287 : 6505918 : PGPROC *proc = NULL;
288 : 6505918 : bool have_sentinel = false;
289 : :
290 : : /*
291 : : * In some use-cases, it is common for awakened processes to immediately
292 : : * re-queue themselves. If we just naively try to reduce the wakeup list
293 : : * to empty, we'll get into a potentially-indefinite loop against such a
294 : : * process. The semantics we really want are just to be sure that we have
295 : : * wakened all processes that were in the list at entry. We can use our
296 : : * own cvWaitLink as a sentinel to detect when we've finished.
297 : : *
298 : : * A seeming flaw in this approach is that someone else might signal the
299 : : * CV and in doing so remove our sentinel entry. But that's fine: since
300 : : * CV waiters are always added and removed in order, that must mean that
301 : : * every previous waiter has been wakened, so we're done. We'll get an
302 : : * extra "set" on our latch from the someone else's signal, which is
303 : : * slightly inefficient but harmless.
304 : : *
305 : : * We can't insert our cvWaitLink as a sentinel if it's already in use in
306 : : * some other proclist. While that's not expected to be true for typical
307 : : * uses of this function, we can deal with it by simply canceling any
308 : : * prepared CV sleep. The next call to ConditionVariableSleep will take
309 : : * care of re-establishing the lost state.
310 : : */
2899 311 [ + + ]: 6505918 : if (cv_sleep_target != NULL)
312 : 89 : ConditionVariableCancelSleep();
313 : :
314 : : /*
315 : : * Inspect the state of the queue. If it's empty, we have nothing to do.
316 : : * If there's exactly one entry, we need only remove and signal that
317 : : * entry. Otherwise, remove the first entry and insert our sentinel.
318 : : */
2903 319 [ + + ]: 6505918 : SpinLockAcquire(&cv->mutex);
320 : : /* While we're here, let's assert we're not in the list. */
321 [ - + ]: 6505918 : Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
322 : :
323 [ + + ]: 6505918 : if (!proclist_is_empty(&cv->wakeup))
324 : : {
325 : 290745 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
326 [ + + ]: 290745 : if (!proclist_is_empty(&cv->wakeup))
327 : : {
328 : 1974 : proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
329 : 1974 : have_sentinel = true;
330 : : }
331 : : }
332 : 6505918 : SpinLockRelease(&cv->mutex);
333 : :
334 : : /* Awaken first waiter, if there was one. */
335 [ + + ]: 6505918 : if (proc != NULL)
336 : 290745 : SetLatch(&proc->procLatch);
337 : :
338 [ + + ]: 6510000 : while (have_sentinel)
339 : : {
340 : : /*
341 : : * Each time through the loop, remove the first wakeup list entry, and
342 : : * signal it unless it's our sentinel. Repeat as long as the sentinel
343 : : * remains in the list.
344 : : *
345 : : * Notice that if someone else removes our sentinel, we will waken one
346 : : * additional process before exiting. That's intentional, because if
347 : : * someone else signals the CV, they may be intending to waken some
348 : : * third process that added itself to the list after we added the
349 : : * sentinel. Better to give a spurious wakeup (which should be
350 : : * harmless beyond wasting some cycles) than to lose a wakeup.
351 : : */
352 : 4082 : proc = NULL;
353 [ + + ]: 4082 : SpinLockAcquire(&cv->mutex);
354 [ + + ]: 4082 : if (!proclist_is_empty(&cv->wakeup))
355 : 4070 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
356 : 4082 : have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
357 : 4082 : SpinLockRelease(&cv->mutex);
358 : :
359 [ + + + + ]: 4082 : if (proc != NULL && proc != MyProc)
360 : 2111 : SetLatch(&proc->procLatch);
361 : : }
3312 rhaas@postgresql.org 362 : 6505918 : }
|