PostgreSQL Source Code  git master
condition_variable.h File Reference
Include dependency graph for condition_variable.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ConditionVariable
 

Functions

void ConditionVariableInit (ConditionVariable *cv)
 
void ConditionVariableSleep (ConditionVariable *cv, uint32 wait_event_info)
 
void ConditionVariableCancelSleep (void)
 
void ConditionVariablePrepareToSleep (ConditionVariable *cv)
 
void ConditionVariableSignal (ConditionVariable *cv)
 
void ConditionVariableBroadcast (ConditionVariable *cv)
 

Function Documentation

◆ ConditionVariableBroadcast()

void ConditionVariableBroadcast ( ConditionVariable cv)

Definition at line 253 of file condition_variable.c.

References Assert, ConditionVariableCancelSleep(), ConditionVariable::mutex, MyProc, PGPROC::pgprocno, PGPROC::procLatch, proclist_contains, proclist_is_empty(), proclist_pop_head_node, proclist_push_tail, SetLatch(), SpinLockAcquire, SpinLockRelease, and ConditionVariable::wakeup.

Referenced by _bt_parallel_done(), BarrierArriveAndWait(), BarrierDetachImpl(), BitmapDoneInitializingSharedState(), ReplicationOriginExitCleanup(), ReplicationSlotAcquire(), ReplicationSlotCleanup(), ReplicationSlotCreate(), ReplicationSlotDropPtr(), ReplicationSlotRelease(), replorigin_session_reset(), and replorigin_session_setup().

254 {
255  int pgprocno = MyProc->pgprocno;
256  PGPROC *proc = NULL;
257  bool have_sentinel = false;
258 
259  /*
260  * In some use-cases, it is common for awakened processes to immediately
261  * re-queue themselves. If we just naively try to reduce the wakeup list
262  * to empty, we'll get into a potentially-indefinite loop against such a
263  * process. The semantics we really want are just to be sure that we have
264  * wakened all processes that were in the list at entry. We can use our
265  * own cvWaitLink as a sentinel to detect when we've finished.
266  *
267  * A seeming flaw in this approach is that someone else might signal the
268  * CV and in doing so remove our sentinel entry. But that's fine: since
269  * CV waiters are always added and removed in order, that must mean that
270  * every previous waiter has been wakened, so we're done. We'll get an
271  * extra "set" on our latch from the someone else's signal, which is
272  * slightly inefficient but harmless.
273  *
274  * We can't insert our cvWaitLink as a sentinel if it's already in use in
275  * some other proclist. While that's not expected to be true for typical
276  * uses of this function, we can deal with it by simply canceling any
277  * prepared CV sleep. The next call to ConditionVariableSleep will take
278  * care of re-establishing the lost state.
279  */
280  if (cv_sleep_target != NULL)
282 
283  /*
284  * Inspect the state of the queue. If it's empty, we have nothing to do.
285  * If there's exactly one entry, we need only remove and signal that
286  * entry. Otherwise, remove the first entry and insert our sentinel.
287  */
288  SpinLockAcquire(&cv->mutex);
289  /* While we're here, let's assert we're not in the list. */
290  Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
291 
292  if (!proclist_is_empty(&cv->wakeup))
293  {
294  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
295  if (!proclist_is_empty(&cv->wakeup))
296  {
297  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
298  have_sentinel = true;
299  }
300  }
301  SpinLockRelease(&cv->mutex);
302 
303  /* Awaken first waiter, if there was one. */
304  if (proc != NULL)
305  SetLatch(&proc->procLatch);
306 
307  while (have_sentinel)
308  {
309  /*
310  * Each time through the loop, remove the first wakeup list entry, and
311  * signal it unless it's our sentinel. Repeat as long as the sentinel
312  * remains in the list.
313  *
314  * Notice that if someone else removes our sentinel, we will waken one
315  * additional process before exiting. That's intentional, because if
316  * someone else signals the CV, they may be intending to waken some
317  * third process that added itself to the list after we added the
318  * sentinel. Better to give a spurious wakeup (which should be
319  * harmless beyond wasting some cycles) than to lose a wakeup.
320  */
321  proc = NULL;
322  SpinLockAcquire(&cv->mutex);
323  if (!proclist_is_empty(&cv->wakeup))
324  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
325  have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
326  SpinLockRelease(&cv->mutex);
327 
328  if (proc != NULL && proc != MyProc)
329  SetLatch(&proc->procLatch);
330  }
331 }
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
Latch procLatch
Definition: proc.h:104
#define SpinLockAcquire(lock)
Definition: spin.h:62
void ConditionVariableCancelSleep(void)
#define SpinLockRelease(lock)
Definition: spin.h:64
static ConditionVariable * cv_sleep_target
#define proclist_pop_head_node(list, link_member)
Definition: proclist.h:193
void SetLatch(volatile Latch *latch)
Definition: latch.c:414
#define Assert(condition)
Definition: c.h:688
static bool proclist_is_empty(proclist_head *list)
Definition: proclist.h:38
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
int pgprocno
Definition: proc.h:110
Definition: proc.h:95
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195

◆ ConditionVariableCancelSleep()

void ConditionVariableCancelSleep ( void  )

Definition at line 206 of file condition_variable.c.

References cv_sleep_target, ConditionVariable::mutex, MyProc, PGPROC::pgprocno, proclist_contains, proclist_delete, SpinLockAcquire, SpinLockRelease, and ConditionVariable::wakeup.

Referenced by _bt_parallel_heapscan(), _bt_parallel_seize(), AbortSubTransaction(), AbortTransaction(), AuxiliaryProcKill(), BackgroundWriterMain(), BarrierArriveAndWait(), BitmapShouldInitializeSharedState(), CheckpointerMain(), ConditionVariableBroadcast(), ConditionVariablePrepareToSleep(), ProcKill(), ReplicationSlotAcquire(), replorigin_drop(), ShutdownAuxiliaryProcess(), WalSndErrorCleanup(), and WalWriterMain().

207 {
209 
210  if (cv == NULL)
211  return;
212 
213  SpinLockAcquire(&cv->mutex);
214  if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
215  proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
216  SpinLockRelease(&cv->mutex);
217 
218  cv_sleep_target = NULL;
219 }
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
#define proclist_delete(list, procno, link_member)
Definition: proclist.h:187
#define SpinLockAcquire(lock)
Definition: spin.h:62
#define SpinLockRelease(lock)
Definition: spin.h:64
static ConditionVariable * cv_sleep_target
int pgprocno
Definition: proc.h:110
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195

◆ ConditionVariableInit()

void ConditionVariableInit ( ConditionVariable cv)

Definition at line 39 of file condition_variable.c.

References ConditionVariable::mutex, proclist_init(), SpinLockInit, and ConditionVariable::wakeup.

Referenced by _bt_begin_parallel(), BarrierInit(), btinitparallelscan(), ExecBitmapHeapInitializeDSM(), ReplicationOriginShmemInit(), and ReplicationSlotsShmemInit().

40 {
41  SpinLockInit(&cv->mutex);
42  proclist_init(&cv->wakeup);
43 }
proclist_head wakeup
#define SpinLockInit(lock)
Definition: spin.h:60
static void proclist_init(proclist_head *list)
Definition: proclist.h:29

◆ ConditionVariablePrepareToSleep()

void ConditionVariablePrepareToSleep ( ConditionVariable cv)

Definition at line 60 of file condition_variable.c.

References AddWaitEventToSet(), ConditionVariableCancelSleep(), CreateWaitEventSet(), ConditionVariable::mutex, MyLatch, MyProc, PGINVALID_SOCKET, PGPROC::pgprocno, proclist_push_tail, ResetLatch(), SpinLockAcquire, SpinLockRelease, TopMemoryContext, ConditionVariable::wakeup, WL_LATCH_SET, and WL_POSTMASTER_DEATH.

Referenced by BarrierArriveAndWait(), ConditionVariableSleep(), and ReplicationSlotAcquire().

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);
74  MyLatch, NULL);
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)
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  */
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 }
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
Definition: latch.c:666
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
static WaitEventSet * cv_wait_event_set
void ResetLatch(volatile Latch *latch)
Definition: latch.c:497
WaitEventSet * CreateWaitEventSet(MemoryContext context, int nevents)
Definition: latch.c:520
#define SpinLockAcquire(lock)
Definition: spin.h:62
void ConditionVariableCancelSleep(void)
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define SpinLockRelease(lock)
Definition: spin.h:64
#define WL_POSTMASTER_DEATH
Definition: latch.h:128
#define PGINVALID_SOCKET
Definition: port.h:33
static ConditionVariable * cv_sleep_target
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
int pgprocno
Definition: proc.h:110
struct Latch * MyLatch
Definition: globals.c:52
#define WL_LATCH_SET
Definition: latch.h:124

◆ ConditionVariableSignal()

void ConditionVariableSignal ( ConditionVariable cv)

Definition at line 230 of file condition_variable.c.

References ConditionVariable::mutex, PGPROC::procLatch, proclist_is_empty(), proclist_pop_head_node, SetLatch(), SpinLockAcquire, SpinLockRelease, and ConditionVariable::wakeup.

Referenced by _bt_parallel_release(), and _bt_parallel_scan_and_sort().

231 {
232  PGPROC *proc = NULL;
233 
234  /* Remove the first process from the wakeup queue (if any). */
235  SpinLockAcquire(&cv->mutex);
236  if (!proclist_is_empty(&cv->wakeup))
237  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
238  SpinLockRelease(&cv->mutex);
239 
240  /* If we found someone sleeping, set their latch to wake them up. */
241  if (proc != NULL)
242  SetLatch(&proc->procLatch);
243 }
proclist_head wakeup
Latch procLatch
Definition: proc.h:104
#define SpinLockAcquire(lock)
Definition: spin.h:62
#define SpinLockRelease(lock)
Definition: spin.h:64
#define proclist_pop_head_node(list, link_member)
Definition: proclist.h:193
void SetLatch(volatile Latch *latch)
Definition: latch.c:414
static bool proclist_is_empty(proclist_head *list)
Definition: proclist.h:38
Definition: proc.h:95

◆ ConditionVariableSleep()

void ConditionVariableSleep ( ConditionVariable cv,
uint32  wait_event_info 
)

Definition at line 123 of file condition_variable.c.

References CHECK_FOR_INTERRUPTS, ConditionVariablePrepareToSleep(), WaitEvent::events, ConditionVariable::mutex, MyLatch, MyProc, PGPROC::pgprocno, proclist_contains, proclist_push_tail, ResetLatch(), SpinLockAcquire, SpinLockRelease, WaitEventSetWait(), ConditionVariable::wakeup, and WL_POSTMASTER_DEATH.

Referenced by _bt_parallel_heapscan(), _bt_parallel_seize(), BarrierArriveAndWait(), BitmapShouldInitializeSharedState(), ReplicationSlotAcquire(), and replorigin_drop().

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  {
146  return;
147  }
148 
149  do
150  {
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  WaitEventSetWait(cv_wait_event_set, -1, &event, 1, wait_event_info);
158 
159  if (event.events & WL_POSTMASTER_DEATH)
160  {
161  /*
162  * Emergency bailout if postmaster has died. This is to avoid the
163  * necessity for manual cleanup of all postmaster children.
164  */
165  exit(1);
166  }
167 
168  /* Reset latch before examining the state of the wait list. */
170 
171  /*
172  * If this process has been taken out of the wait list, then we know
173  * that it has been signaled by ConditionVariableSignal (or
174  * ConditionVariableBroadcast), so we should return to the caller. But
175  * that doesn't guarantee that the exit condition is met, only that we
176  * ought to check it. So we must put the process back into the wait
177  * list, to ensure we don't miss any additional wakeup occurring while
178  * the caller checks its exit condition. We can take ourselves out of
179  * the wait list only when the caller calls
180  * ConditionVariableCancelSleep.
181  *
182  * If we're still in the wait list, then the latch must have been set
183  * by something other than ConditionVariableSignal; though we don't
184  * guarantee not to return spuriously, we'll avoid this obvious case.
185  */
186  SpinLockAcquire(&cv->mutex);
187  if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
188  {
189  done = true;
190  proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
191  }
192  SpinLockRelease(&cv->mutex);
193  } while (!done);
194 }
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
static WaitEventSet * cv_wait_event_set
void ResetLatch(volatile Latch *latch)
Definition: latch.c:497
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
#define SpinLockAcquire(lock)
Definition: spin.h:62
uint32 events
Definition: latch.h:143
#define SpinLockRelease(lock)
Definition: spin.h:64
#define WL_POSTMASTER_DEATH
Definition: latch.h:128
static ConditionVariable * cv_sleep_target
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
int pgprocno
Definition: proc.h:110
struct Latch * MyLatch
Definition: globals.c:52
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
int WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents, uint32 wait_event_info)
Definition: latch.c:921
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195