PostgreSQL Source Code  git master
condition_variable.c
Go to the documentation of this file.
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"
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. */
30 
31 /*
32  * Initialize a condition variable.
33  */
34 void
36 {
37  SpinLockInit(&cv->mutex);
38  proclist_init(&cv->wakeup);
39 }
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
57 {
58  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  */
68  if (cv_sleep_target != NULL)
70 
71  /* Record the condition variable on which we will sleep. */
72  cv_sleep_target = cv;
73 
74  /* Add myself to the wait queue. */
75  SpinLockAcquire(&cv->mutex);
76  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
77  SpinLockRelease(&cv->mutex);
78 }
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
97 {
98  (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
99  wait_event_info);
100 }
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
113  uint32 wait_event_info)
114 {
115  long cur_timeout = -1;
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  */
135  if (cv_sleep_target != cv)
136  {
138  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  if (timeout >= 0)
146  {
148  Assert(timeout >= 0 && timeout <= INT_MAX);
149  cur_timeout = timeout;
150  wait_events = WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH;
151  }
152  else
153  wait_events = WL_LATCH_SET | WL_EXIT_ON_PM_DEATH;
154 
155  while (true)
156  {
157  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  */
163  (void) WaitLatch(MyLatch, wait_events, cur_timeout, wait_event_info);
164 
165  /* Reset latch before examining the state of the wait list. */
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  */
183  SpinLockAcquire(&cv->mutex);
184  if (!proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
185  {
186  done = true;
187  proclist_push_tail(&cv->wakeup, MyProcNumber, cvWaitLink);
188  }
189  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  */
197  if (cv != cv_sleep_target)
198  done = true;
199 
200  /* We were signaled, so return */
201  if (done)
202  return false;
203 
204  /* If we're not done, update cur_timeout for next iteration */
205  if (timeout >= 0)
206  {
207  INSTR_TIME_SET_CURRENT(cur_time);
208  INSTR_TIME_SUBTRACT(cur_time, start_time);
209  cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
210 
211  /* Have we crossed the timeout threshold? */
212  if (cur_timeout <= 0)
213  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
231 {
233  bool signaled = false;
234 
235  if (cv == NULL)
236  return false;
237 
238  SpinLockAcquire(&cv->mutex);
239  if (proclist_contains(&cv->wakeup, MyProcNumber, cvWaitLink))
240  proclist_delete(&cv->wakeup, MyProcNumber, cvWaitLink);
241  else
242  signaled = true;
243  SpinLockRelease(&cv->mutex);
244 
245  cv_sleep_target = NULL;
246 
247  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
260 {
261  PGPROC *proc = NULL;
262 
263  /* Remove the first process from the wakeup queue (if any). */
264  SpinLockAcquire(&cv->mutex);
265  if (!proclist_is_empty(&cv->wakeup))
266  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
267  SpinLockRelease(&cv->mutex);
268 
269  /* If we found someone sleeping, set their latch to wake them up. */
270  if (proc != NULL)
271  SetLatch(&proc->procLatch);
272 }
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
283 {
284  int pgprocno = MyProcNumber;
285  PGPROC *proc = NULL;
286  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  */
309  if (cv_sleep_target != NULL)
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  */
317  SpinLockAcquire(&cv->mutex);
318  /* While we're here, let's assert we're not in the list. */
319  Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
320 
321  if (!proclist_is_empty(&cv->wakeup))
322  {
323  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
324  if (!proclist_is_empty(&cv->wakeup))
325  {
326  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
327  have_sentinel = true;
328  }
329  }
330  SpinLockRelease(&cv->mutex);
331 
332  /* Awaken first waiter, if there was one. */
333  if (proc != NULL)
334  SetLatch(&proc->procLatch);
335 
336  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  proc = NULL;
351  SpinLockAcquire(&cv->mutex);
352  if (!proclist_is_empty(&cv->wakeup))
353  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
354  have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
355  SpinLockRelease(&cv->mutex);
356 
357  if (proc != NULL && proc != MyProc)
358  SetLatch(&proc->procLatch);
359  }
360 }
unsigned int uint32
Definition: c.h:506
#define Assert(condition)
Definition: c.h:858
bool ConditionVariableCancelSleep(void)
bool ConditionVariableTimedSleep(ConditionVariable *cv, long timeout, uint32 wait_event_info)
static ConditionVariable * cv_sleep_target
void ConditionVariableBroadcast(ConditionVariable *cv)
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
void ConditionVariableInit(ConditionVariable *cv)
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
void ConditionVariableSignal(ConditionVariable *cv)
ProcNumber MyProcNumber
Definition: globals.c:88
struct Latch * MyLatch
Definition: globals.c:61
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:181
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:191
void SetLatch(Latch *latch)
Definition: latch.c:632
void ResetLatch(Latch *latch)
Definition: latch.c:724
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition: latch.c:517
#define WL_TIMEOUT
Definition: latch.h:130
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:132
#define WL_LATCH_SET
Definition: latch.h:127
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static time_t start_time
Definition: pg_ctl.c:94
#define proclist_pop_head_node(list, link_member)
Definition: proclist.h:193
#define proclist_delete(list, procno, link_member)
Definition: proclist.h:187
static void proclist_init(proclist_head *list)
Definition: proclist.h:29
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195
static bool proclist_is_empty(const proclist_head *list)
Definition: proclist.h:38
#define SpinLockInit(lock)
Definition: spin.h:60
#define SpinLockRelease(lock)
Definition: spin.h:64
#define SpinLockAcquire(lock)
Definition: spin.h:62
PGPROC * MyProc
Definition: proc.c:66
proclist_head wakeup
Definition: proc.h:157
Latch procLatch
Definition: proc.h:164