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-2019, 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 | ConditionVariableInit(ConditionVariable *cv) |
40 | { |
41 | SpinLockInit(&cv->mutex); |
42 | proclist_init(&cv->wakeup); |
43 | } |
44 | |
45 | /* |
46 | * Prepare to wait on a given condition variable. |
47 | * |
48 | * This can optionally be called before entering a test/sleep loop. |
49 | * Doing so is more efficient if we'll need to sleep at least once. |
50 | * However, if the first test of the exit condition is likely to succeed, |
51 | * it's more efficient to omit the ConditionVariablePrepareToSleep call. |
52 | * See comments in ConditionVariableSleep for more detail. |
53 | * |
54 | * Caution: "before entering the loop" means you *must* test the exit |
55 | * condition between calling ConditionVariablePrepareToSleep and calling |
56 | * ConditionVariableSleep. If that is inconvenient, omit calling |
57 | * ConditionVariablePrepareToSleep. |
58 | */ |
59 | void |
60 | ConditionVariablePrepareToSleep(ConditionVariable *cv) |
61 | { |
62 | int pgprocno = MyProc->pgprocno; |
63 | |
64 | /* |
65 | * If first time through in this process, create a WaitEventSet, which |
66 | * we'll reuse for all condition variable sleeps. |
67 | */ |
68 | if (cv_wait_event_set == NULL) |
69 | { |
70 | WaitEventSet *new_event_set; |
71 | |
72 | new_event_set = CreateWaitEventSet(TopMemoryContext, 2); |
73 | AddWaitEventToSet(new_event_set, WL_LATCH_SET, PGINVALID_SOCKET, |
74 | MyLatch, NULL); |
75 | AddWaitEventToSet(new_event_set, WL_EXIT_ON_PM_DEATH, PGINVALID_SOCKET, |
76 | NULL, NULL); |
77 | /* Don't set cv_wait_event_set until we have a correct WES. */ |
78 | cv_wait_event_set = new_event_set; |
79 | } |
80 | |
81 | /* |
82 | * If some other sleep is already prepared, cancel it; this is necessary |
83 | * because we have just one static variable tracking the prepared sleep, |
84 | * and also only one cvWaitLink in our PGPROC. It's okay to do this |
85 | * because whenever control does return to the other test-and-sleep loop, |
86 | * its ConditionVariableSleep call will just re-establish that sleep as |
87 | * the prepared one. |
88 | */ |
89 | if (cv_sleep_target != NULL) |
90 | ConditionVariableCancelSleep(); |
91 | |
92 | /* Record the condition variable on which we will sleep. */ |
93 | cv_sleep_target = cv; |
94 | |
95 | /* |
96 | * Reset my latch before adding myself to the queue, to ensure that we |
97 | * don't miss a wakeup that occurs immediately. |
98 | */ |
99 | ResetLatch(MyLatch); |
100 | |
101 | /* Add myself to the wait queue. */ |
102 | SpinLockAcquire(&cv->mutex); |
103 | proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink); |
104 | SpinLockRelease(&cv->mutex); |
105 | } |
106 | |
107 | /* |
108 | * Wait for the given condition variable to be signaled. |
109 | * |
110 | * This should be called in a predicate loop that tests for a specific exit |
111 | * condition and otherwise sleeps, like so: |
112 | * |
113 | * ConditionVariablePrepareToSleep(cv); // optional |
114 | * while (condition for which we are waiting is not true) |
115 | * ConditionVariableSleep(cv, wait_event_info); |
116 | * ConditionVariableCancelSleep(); |
117 | * |
118 | * wait_event_info should be a value from one of the WaitEventXXX enums |
119 | * defined in pgstat.h. This controls the contents of pg_stat_activity's |
120 | * wait_event_type and wait_event columns while waiting. |
121 | */ |
122 | void |
123 | ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info) |
124 | { |
125 | WaitEvent event; |
126 | bool done = false; |
127 | |
128 | /* |
129 | * If the caller didn't prepare to sleep explicitly, then do so now and |
130 | * return immediately. The caller's predicate loop should immediately |
131 | * call again if its exit condition is not yet met. This will result in |
132 | * the exit condition being tested twice before we first sleep. The extra |
133 | * test can be prevented by calling ConditionVariablePrepareToSleep(cv) |
134 | * first. Whether it's worth doing that depends on whether you expect the |
135 | * exit condition to be met initially, in which case skipping the prepare |
136 | * is recommended because it avoids manipulations of the wait list, or not |
137 | * met initially, in which case preparing first is better because it |
138 | * avoids one extra test of the exit condition. |
139 | * |
140 | * If we are currently prepared to sleep on some other CV, we just cancel |
141 | * that and prepare this one; see ConditionVariablePrepareToSleep. |
142 | */ |
143 | if (cv_sleep_target != cv) |
144 | { |
145 | ConditionVariablePrepareToSleep(cv); |
146 | return; |
147 | } |
148 | |
149 | do |
150 | { |
151 | CHECK_FOR_INTERRUPTS(); |
152 | |
153 | /* |
154 | * Wait for latch to be set. (If we're awakened for some other |
155 | * reason, the code below will cope anyway.) |
156 | */ |
157 | (void) WaitEventSetWait(cv_wait_event_set, -1, &event, 1, |
158 | wait_event_info); |
159 | |
160 | /* Reset latch before examining the state of the wait list. */ |
161 | ResetLatch(MyLatch); |
162 | |
163 | /* |
164 | * If this process has been taken out of the wait list, then we know |
165 | * that it has been signaled by ConditionVariableSignal (or |
166 | * ConditionVariableBroadcast), so we should return to the caller. But |
167 | * that doesn't guarantee that the exit condition is met, only that we |
168 | * ought to check it. So we must put the process back into the wait |
169 | * list, to ensure we don't miss any additional wakeup occurring while |
170 | * the caller checks its exit condition. We can take ourselves out of |
171 | * the wait list only when the caller calls |
172 | * ConditionVariableCancelSleep. |
173 | * |
174 | * If we're still in the wait list, then the latch must have been set |
175 | * by something other than ConditionVariableSignal; though we don't |
176 | * guarantee not to return spuriously, we'll avoid this obvious case. |
177 | */ |
178 | SpinLockAcquire(&cv->mutex); |
179 | if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink)) |
180 | { |
181 | done = true; |
182 | proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink); |
183 | } |
184 | SpinLockRelease(&cv->mutex); |
185 | } while (!done); |
186 | } |
187 | |
188 | /* |
189 | * Cancel any pending sleep operation. |
190 | * |
191 | * We just need to remove ourselves from the wait queue of any condition |
192 | * variable for which we have previously prepared a sleep. |
193 | * |
194 | * Do nothing if nothing is pending; this allows this function to be called |
195 | * during transaction abort to clean up any unfinished CV sleep. |
196 | */ |
197 | void |
198 | ConditionVariableCancelSleep(void) |
199 | { |
200 | ConditionVariable *cv = cv_sleep_target; |
201 | |
202 | if (cv == NULL) |
203 | return; |
204 | |
205 | SpinLockAcquire(&cv->mutex); |
206 | if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink)) |
207 | proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink); |
208 | SpinLockRelease(&cv->mutex); |
209 | |
210 | cv_sleep_target = NULL; |
211 | } |
212 | |
213 | /* |
214 | * Wake up the oldest process sleeping on the CV, if there is any. |
215 | * |
216 | * Note: it's difficult to tell whether this has any real effect: we know |
217 | * whether we took an entry off the list, but the entry might only be a |
218 | * sentinel. Hence, think twice before proposing that this should return |
219 | * a flag telling whether it woke somebody. |
220 | */ |
221 | void |
222 | ConditionVariableSignal(ConditionVariable *cv) |
223 | { |
224 | PGPROC *proc = NULL; |
225 | |
226 | /* Remove the first process from the wakeup queue (if any). */ |
227 | SpinLockAcquire(&cv->mutex); |
228 | if (!proclist_is_empty(&cv->wakeup)) |
229 | proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink); |
230 | SpinLockRelease(&cv->mutex); |
231 | |
232 | /* If we found someone sleeping, set their latch to wake them up. */ |
233 | if (proc != NULL) |
234 | SetLatch(&proc->procLatch); |
235 | } |
236 | |
237 | /* |
238 | * Wake up all processes sleeping on the given CV. |
239 | * |
240 | * This guarantees to wake all processes that were sleeping on the CV |
241 | * at time of call, but processes that add themselves to the list mid-call |
242 | * will typically not get awakened. |
243 | */ |
244 | void |
245 | ConditionVariableBroadcast(ConditionVariable *cv) |
246 | { |
247 | int pgprocno = MyProc->pgprocno; |
248 | PGPROC *proc = NULL; |
249 | bool have_sentinel = false; |
250 | |
251 | /* |
252 | * In some use-cases, it is common for awakened processes to immediately |
253 | * re-queue themselves. If we just naively try to reduce the wakeup list |
254 | * to empty, we'll get into a potentially-indefinite loop against such a |
255 | * process. The semantics we really want are just to be sure that we have |
256 | * wakened all processes that were in the list at entry. We can use our |
257 | * own cvWaitLink as a sentinel to detect when we've finished. |
258 | * |
259 | * A seeming flaw in this approach is that someone else might signal the |
260 | * CV and in doing so remove our sentinel entry. But that's fine: since |
261 | * CV waiters are always added and removed in order, that must mean that |
262 | * every previous waiter has been wakened, so we're done. We'll get an |
263 | * extra "set" on our latch from the someone else's signal, which is |
264 | * slightly inefficient but harmless. |
265 | * |
266 | * We can't insert our cvWaitLink as a sentinel if it's already in use in |
267 | * some other proclist. While that's not expected to be true for typical |
268 | * uses of this function, we can deal with it by simply canceling any |
269 | * prepared CV sleep. The next call to ConditionVariableSleep will take |
270 | * care of re-establishing the lost state. |
271 | */ |
272 | if (cv_sleep_target != NULL) |
273 | ConditionVariableCancelSleep(); |
274 | |
275 | /* |
276 | * Inspect the state of the queue. If it's empty, we have nothing to do. |
277 | * If there's exactly one entry, we need only remove and signal that |
278 | * entry. Otherwise, remove the first entry and insert our sentinel. |
279 | */ |
280 | SpinLockAcquire(&cv->mutex); |
281 | /* While we're here, let's assert we're not in the list. */ |
282 | Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink)); |
283 | |
284 | if (!proclist_is_empty(&cv->wakeup)) |
285 | { |
286 | proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink); |
287 | if (!proclist_is_empty(&cv->wakeup)) |
288 | { |
289 | proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink); |
290 | have_sentinel = true; |
291 | } |
292 | } |
293 | SpinLockRelease(&cv->mutex); |
294 | |
295 | /* Awaken first waiter, if there was one. */ |
296 | if (proc != NULL) |
297 | SetLatch(&proc->procLatch); |
298 | |
299 | while (have_sentinel) |
300 | { |
301 | /* |
302 | * Each time through the loop, remove the first wakeup list entry, and |
303 | * signal it unless it's our sentinel. Repeat as long as the sentinel |
304 | * remains in the list. |
305 | * |
306 | * Notice that if someone else removes our sentinel, we will waken one |
307 | * additional process before exiting. That's intentional, because if |
308 | * someone else signals the CV, they may be intending to waken some |
309 | * third process that added itself to the list after we added the |
310 | * sentinel. Better to give a spurious wakeup (which should be |
311 | * harmless beyond wasting some cycles) than to lose a wakeup. |
312 | */ |
313 | proc = NULL; |
314 | SpinLockAcquire(&cv->mutex); |
315 | if (!proclist_is_empty(&cv->wakeup)) |
316 | proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink); |
317 | have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink); |
318 | SpinLockRelease(&cv->mutex); |
319 | |
320 | if (proc != NULL && proc != MyProc) |
321 | SetLatch(&proc->procLatch); |
322 | } |
323 | } |
324 | |