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-2024, 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/proc.h"
25 : : #include "storage/proclist.h"
26 : : #include "storage/spin.h"
27 : :
28 : : /* Initially, we are not prepared to sleep on any condition variable. */
29 : : static ConditionVariable *cv_sleep_target = NULL;
30 : :
31 : : /*
32 : : * Initialize a condition variable.
33 : : */
34 : : void
2700 rhaas@postgresql.org 35 :CBC 8382932 : ConditionVariableInit(ConditionVariable *cv)
36 : : {
37 : 8382932 : SpinLockInit(&cv->mutex);
38 : 8382932 : proclist_init(&cv->wakeup);
39 : 8382932 : }
40 : :
41 : : /*
42 : : * Prepare to wait on a given condition variable.
43 : : *
44 : : * This can optionally be called before entering a test/sleep loop.
45 : : * Doing so is more efficient if we'll need to sleep at least once.
46 : : * However, if the first test of the exit condition is likely to succeed,
47 : : * it's more efficient to omit the ConditionVariablePrepareToSleep call.
48 : : * See comments in ConditionVariableSleep for more detail.
49 : : *
50 : : * Caution: "before entering the loop" means you *must* test the exit
51 : : * condition between calling ConditionVariablePrepareToSleep and calling
52 : : * ConditionVariableSleep. If that is inconvenient, omit calling
53 : : * ConditionVariablePrepareToSleep.
54 : : */
55 : : void
56 : 73594 : ConditionVariablePrepareToSleep(ConditionVariable *cv)
57 : : {
52 heikki.linnakangas@i 58 :GNC 73594 : int pgprocno = MyProcNumber;
59 : :
60 : : /*
61 : : * If some other sleep is already prepared, cancel it; this is necessary
62 : : * because we have just one static variable tracking the prepared sleep,
63 : : * and also only one cvWaitLink in our PGPROC. It's okay to do this
64 : : * because whenever control does return to the other test-and-sleep loop,
65 : : * its ConditionVariableSleep call will just re-establish that sleep as
66 : : * the prepared one.
67 : : */
2287 tgl@sss.pgh.pa.us 68 [ - + ]:CBC 73594 : if (cv_sleep_target != NULL)
2287 tgl@sss.pgh.pa.us 69 :UBC 0 : ConditionVariableCancelSleep();
70 : :
71 : : /* Record the condition variable on which we will sleep. */
2700 rhaas@postgresql.org 72 :CBC 73594 : cv_sleep_target = cv;
73 : :
74 : : /* Add myself to the wait queue. */
75 [ + + ]: 73594 : SpinLockAcquire(&cv->mutex);
2288 tgl@sss.pgh.pa.us 76 : 73594 : proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
2700 rhaas@postgresql.org 77 : 73594 : SpinLockRelease(&cv->mutex);
78 : 73594 : }
79 : :
80 : : /*
81 : : * Wait for the given condition variable to be signaled.
82 : : *
83 : : * This should be called in a predicate loop that tests for a specific exit
84 : : * condition and otherwise sleeps, like so:
85 : : *
86 : : * ConditionVariablePrepareToSleep(cv); // optional
87 : : * while (condition for which we are waiting is not true)
88 : : * ConditionVariableSleep(cv, wait_event_info);
89 : : * ConditionVariableCancelSleep();
90 : : *
91 : : * wait_event_info should be a value from one of the WaitEventXXX enums
92 : : * defined in pgstat.h. This controls the contents of pg_stat_activity's
93 : : * wait_event_type and wait_event columns while waiting.
94 : : */
95 : : void
96 : 5413 : ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
97 : : {
1737 tmunro@postgresql.or 98 : 5413 : (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
99 : : wait_event_info);
100 : 5413 : }
101 : :
102 : : /*
103 : : * Wait for a condition variable to be signaled or a timeout to be reached.
104 : : *
105 : : * The "timeout" is given in milliseconds.
106 : : *
107 : : * Returns true when timeout expires, otherwise returns false.
108 : : *
109 : : * See ConditionVariableSleep() for general usage.
110 : : */
111 : : bool
112 : 5794 : ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
113 : : uint32 wait_event_info)
114 : : {
115 : 5794 : 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 : : */
2287 tgl@sss.pgh.pa.us 135 [ + + ]: 5794 : if (cv_sleep_target != cv)
136 : : {
2700 rhaas@postgresql.org 137 : 310 : ConditionVariablePrepareToSleep(cv);
1737 tmunro@postgresql.or 138 : 310 : 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 [ + + ]: 5484 : if (timeout >= 0)
146 : : {
147 : 190 : INSTR_TIME_SET_CURRENT(start_time);
148 [ + - - + ]: 190 : Assert(timeout >= 0 && timeout <= INT_MAX);
149 : 190 : cur_timeout = timeout;
1354 150 : 190 : wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
151 : : }
152 : : else
153 : 5294 : wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
154 : :
155 : : while (true)
1737 156 : 812 : {
157 : 6296 : 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 : : */
1354 163 : 6296 : (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
164 : :
165 : : /* Reset latch before examining the state of the wait list. */
2504 andres@anarazel.de 166 : 6290 : 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 : : */
2700 rhaas@postgresql.org 183 [ + + ]: 6290 : SpinLockAcquire(&cv->mutex);
52 heikki.linnakangas@i 184 [ + + ]:GNC 6290 : if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
185 : : {
2700 rhaas@postgresql.org 186 :CBC 5409 : done = true;
52 heikki.linnakangas@i 187 :GNC 5409 : proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
188 : : }
2700 rhaas@postgresql.org 189 :CBC 6290 : 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 : : */
1140 tmunro@postgresql.or 196 [ + + ]: 6290 : CHECK_FOR_INTERRUPTS();
197 [ + + ]: 6290 : if (cv != cv_sleep_target)
198 : 72 : done = true;
199 : :
200 : : /* We were signaled, so return */
1737 201 [ + + ]: 6290 : if (done)
202 : 5476 : return false;
203 : :
204 : : /* If we're not done, update cur_timeout for next iteration */
205 [ + + ]: 814 : if (timeout >= 0)
206 : : {
207 : 41 : INSTR_TIME_SET_CURRENT(cur_time);
208 : 41 : INSTR_TIME_SUBTRACT(cur_time, start_time);
209 : 41 : cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
210 : :
211 : : /* Have we crossed the timeout threshold? */
212 [ + + ]: 41 : 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 : : * Return true if we've been signaled.
228 : : */
229 : : bool
2700 rhaas@postgresql.org 230 : 128441 : ConditionVariableCancelSleep(void)
231 : : {
232 : 128441 : ConditionVariable *cv = cv_sleep_target;
1737 tmunro@postgresql.or 233 : 128441 : bool signaled = false;
234 : :
2700 rhaas@postgresql.org 235 [ + + ]: 128441 : if (cv == NULL)
243 tmunro@postgresql.or 236 : 54902 : return false;
237 : :
2700 rhaas@postgresql.org 238 [ + + ]: 73539 : SpinLockAcquire(&cv->mutex);
52 heikki.linnakangas@i 239 [ + + ]:GNC 73539 : if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
240 : 50485 : proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
241 : : else
1737 tmunro@postgresql.or 242 :CBC 23054 : signaled = true;
2700 rhaas@postgresql.org 243 : 73539 : SpinLockRelease(&cv->mutex);
244 : :
245 : 73539 : cv_sleep_target = NULL;
246 : :
243 tmunro@postgresql.or 247 : 73539 : return signaled;
248 : : }
249 : :
250 : : /*
251 : : * Wake up the oldest process sleeping on the CV, if there is any.
252 : : *
253 : : * Note: it's difficult to tell whether this has any real effect: we know
254 : : * whether we took an entry off the list, but the entry might only be a
255 : : * sentinel. Hence, think twice before proposing that this should return
256 : : * a flag telling whether it woke somebody.
257 : : */
258 : : void
2700 rhaas@postgresql.org 259 : 811 : ConditionVariableSignal(ConditionVariable *cv)
260 : : {
2524 bruce@momjian.us 261 : 811 : PGPROC *proc = NULL;
262 : :
263 : : /* Remove the first process from the wakeup queue (if any). */
2700 rhaas@postgresql.org 264 [ + + ]: 811 : SpinLockAcquire(&cv->mutex);
265 [ + + ]: 811 : if (!proclist_is_empty(&cv->wakeup))
266 : 105 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
267 : 811 : SpinLockRelease(&cv->mutex);
268 : :
269 : : /* If we found someone sleeping, set their latch to wake them up. */
270 [ + + ]: 811 : if (proc != NULL)
271 : 105 : SetLatch(&proc->procLatch);
272 : 811 : }
273 : :
274 : : /*
275 : : * Wake up all processes sleeping on the given CV.
276 : : *
277 : : * This guarantees to wake all processes that were sleeping on the CV
278 : : * at time of call, but processes that add themselves to the list mid-call
279 : : * will typically not get awakened.
280 : : */
281 : : void
282 : 4897077 : ConditionVariableBroadcast(ConditionVariable *cv)
283 : : {
52 heikki.linnakangas@i 284 :GNC 4897077 : int pgprocno = MyProcNumber;
2291 tgl@sss.pgh.pa.us 285 :CBC 4897077 : PGPROC *proc = NULL;
286 : 4897077 : bool have_sentinel = false;
287 : :
288 : : /*
289 : : * In some use-cases, it is common for awakened processes to immediately
290 : : * re-queue themselves. If we just naively try to reduce the wakeup list
291 : : * to empty, we'll get into a potentially-indefinite loop against such a
292 : : * process. The semantics we really want are just to be sure that we have
293 : : * wakened all processes that were in the list at entry. We can use our
294 : : * own cvWaitLink as a sentinel to detect when we've finished.
295 : : *
296 : : * A seeming flaw in this approach is that someone else might signal the
297 : : * CV and in doing so remove our sentinel entry. But that's fine: since
298 : : * CV waiters are always added and removed in order, that must mean that
299 : : * every previous waiter has been wakened, so we're done. We'll get an
300 : : * extra "set" on our latch from the someone else's signal, which is
301 : : * slightly inefficient but harmless.
302 : : *
303 : : * We can't insert our cvWaitLink as a sentinel if it's already in use in
304 : : * some other proclist. While that's not expected to be true for typical
305 : : * uses of this function, we can deal with it by simply canceling any
306 : : * prepared CV sleep. The next call to ConditionVariableSleep will take
307 : : * care of re-establishing the lost state.
308 : : */
2287 309 [ + + ]: 4897077 : if (cv_sleep_target != NULL)
310 : 86 : ConditionVariableCancelSleep();
311 : :
312 : : /*
313 : : * Inspect the state of the queue. If it's empty, we have nothing to do.
314 : : * If there's exactly one entry, we need only remove and signal that
315 : : * entry. Otherwise, remove the first entry and insert our sentinel.
316 : : */
2291 317 [ + + ]: 4897077 : SpinLockAcquire(&cv->mutex);
318 : : /* While we're here, let's assert we're not in the list. */
319 [ - + ]: 4897077 : Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
320 : :
321 [ + + ]: 4897077 : if (!proclist_is_empty(&cv->wakeup))
322 : : {
323 : 27451 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
324 [ + + ]: 27451 : if (!proclist_is_empty(&cv->wakeup))
325 : : {
326 : 806 : proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
327 : 806 : have_sentinel = true;
328 : : }
329 : : }
330 : 4897077 : SpinLockRelease(&cv->mutex);
331 : :
332 : : /* Awaken first waiter, if there was one. */
333 [ + + ]: 4897077 : if (proc != NULL)
334 : 27451 : SetLatch(&proc->procLatch);
335 : :
336 [ + + ]: 4898826 : while (have_sentinel)
337 : : {
338 : : /*
339 : : * Each time through the loop, remove the first wakeup list entry, and
340 : : * signal it unless it's our sentinel. Repeat as long as the sentinel
341 : : * remains in the list.
342 : : *
343 : : * Notice that if someone else removes our sentinel, we will waken one
344 : : * additional process before exiting. That's intentional, because if
345 : : * someone else signals the CV, they may be intending to waken some
346 : : * third process that added itself to the list after we added the
347 : : * sentinel. Better to give a spurious wakeup (which should be
348 : : * harmless beyond wasting some cycles) than to lose a wakeup.
349 : : */
350 : 1749 : proc = NULL;
351 [ + + ]: 1749 : SpinLockAcquire(&cv->mutex);
352 [ + - ]: 1749 : if (!proclist_is_empty(&cv->wakeup))
353 : 1749 : proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
354 : 1749 : have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
355 : 1749 : SpinLockRelease(&cv->mutex);
356 : :
357 [ + - + + ]: 1749 : if (proc != NULL && proc != MyProc)
358 : 943 : SetLatch(&proc->procLatch);
359 : : }
2700 rhaas@postgresql.org 360 : 4897077 : }
|