PostgreSQL Source Code  git master
condition_variable.c File Reference
#include "postgres.h"
#include "miscadmin.h"
#include "portability/instr_time.h"
#include "storage/condition_variable.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "storage/proclist.h"
#include "storage/spin.h"
#include "utils/memutils.h"
Include dependency graph for condition_variable.c:

Go to the source code of this file.

Functions

void ConditionVariableInit (ConditionVariable *cv)
 
void ConditionVariablePrepareToSleep (ConditionVariable *cv)
 
void ConditionVariableSleep (ConditionVariable *cv, uint32 wait_event_info)
 
bool ConditionVariableTimedSleep (ConditionVariable *cv, long timeout, uint32 wait_event_info)
 
void ConditionVariableCancelSleep (void)
 
void ConditionVariableSignal (ConditionVariable *cv)
 
void ConditionVariableBroadcast (ConditionVariable *cv)
 

Variables

static ConditionVariablecv_sleep_target = NULL
 

Function Documentation

◆ ConditionVariableBroadcast()

void ConditionVariableBroadcast ( ConditionVariable cv)

Definition at line 279 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(), CheckpointerMain(), ReplicationOriginExitCleanup(), ReplicationSlotAcquireInternal(), ReplicationSlotCleanup(), ReplicationSlotCreate(), ReplicationSlotDropPtr(), ReplicationSlotRelease(), replorigin_session_reset(), and replorigin_session_setup().

280 {
281  int pgprocno = MyProc->pgprocno;
282  PGPROC *proc = NULL;
283  bool have_sentinel = false;
284 
285  /*
286  * In some use-cases, it is common for awakened processes to immediately
287  * re-queue themselves. If we just naively try to reduce the wakeup list
288  * to empty, we'll get into a potentially-indefinite loop against such a
289  * process. The semantics we really want are just to be sure that we have
290  * wakened all processes that were in the list at entry. We can use our
291  * own cvWaitLink as a sentinel to detect when we've finished.
292  *
293  * A seeming flaw in this approach is that someone else might signal the
294  * CV and in doing so remove our sentinel entry. But that's fine: since
295  * CV waiters are always added and removed in order, that must mean that
296  * every previous waiter has been wakened, so we're done. We'll get an
297  * extra "set" on our latch from the someone else's signal, which is
298  * slightly inefficient but harmless.
299  *
300  * We can't insert our cvWaitLink as a sentinel if it's already in use in
301  * some other proclist. While that's not expected to be true for typical
302  * uses of this function, we can deal with it by simply canceling any
303  * prepared CV sleep. The next call to ConditionVariableSleep will take
304  * care of re-establishing the lost state.
305  */
306  if (cv_sleep_target != NULL)
308 
309  /*
310  * Inspect the state of the queue. If it's empty, we have nothing to do.
311  * If there's exactly one entry, we need only remove and signal that
312  * entry. Otherwise, remove the first entry and insert our sentinel.
313  */
314  SpinLockAcquire(&cv->mutex);
315  /* While we're here, let's assert we're not in the list. */
316  Assert(!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink));
317 
318  if (!proclist_is_empty(&cv->wakeup))
319  {
320  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
321  if (!proclist_is_empty(&cv->wakeup))
322  {
323  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
324  have_sentinel = true;
325  }
326  }
327  SpinLockRelease(&cv->mutex);
328 
329  /* Awaken first waiter, if there was one. */
330  if (proc != NULL)
331  SetLatch(&proc->procLatch);
332 
333  while (have_sentinel)
334  {
335  /*
336  * Each time through the loop, remove the first wakeup list entry, and
337  * signal it unless it's our sentinel. Repeat as long as the sentinel
338  * remains in the list.
339  *
340  * Notice that if someone else removes our sentinel, we will waken one
341  * additional process before exiting. That's intentional, because if
342  * someone else signals the CV, they may be intending to waken some
343  * third process that added itself to the list after we added the
344  * sentinel. Better to give a spurious wakeup (which should be
345  * harmless beyond wasting some cycles) than to lose a wakeup.
346  */
347  proc = NULL;
348  SpinLockAcquire(&cv->mutex);
349  if (!proclist_is_empty(&cv->wakeup))
350  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
351  have_sentinel = proclist_contains(&cv->wakeup, pgprocno, cvWaitLink);
352  SpinLockRelease(&cv->mutex);
353 
354  if (proc != NULL && proc != MyProc)
355  SetLatch(&proc->procLatch);
356  }
357 }
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
void SetLatch(Latch *latch)
Definition: latch.c:505
Latch procLatch
Definition: proc.h:121
#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
#define Assert(condition)
Definition: c.h:746
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:141
Definition: proc.h:112
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195

◆ ConditionVariableCancelSleep()

void ConditionVariableCancelSleep ( void  )

Definition at line 221 of file condition_variable.c.

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

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

222 {
224  bool signaled = false;
225 
226  if (cv == NULL)
227  return;
228 
229  SpinLockAcquire(&cv->mutex);
230  if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
231  proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
232  else
233  signaled = true;
234  SpinLockRelease(&cv->mutex);
235 
236  /*
237  * If we've received a signal, pass it on to another waiting process, if
238  * there is one. Otherwise a call to ConditionVariableSignal() might get
239  * lost, despite there being another process ready to handle it.
240  */
241  if (signaled)
243 
244  cv_sleep_target = NULL;
245 }
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
void ConditionVariableSignal(ConditionVariable *cv)
#define SpinLockRelease(lock)
Definition: spin.h:64
static ConditionVariable * cv_sleep_target
int pgprocno
Definition: proc.h:141
static volatile sig_atomic_t signaled
Definition: pg_standby.c:52
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195

◆ ConditionVariableInit()

void ConditionVariableInit ( ConditionVariable cv)

◆ ConditionVariablePrepareToSleep()

void ConditionVariablePrepareToSleep ( ConditionVariable cv)

Definition at line 58 of file condition_variable.c.

References ConditionVariableCancelSleep(), ConditionVariable::mutex, MyProc, PGPROC::pgprocno, proclist_push_tail, SpinLockAcquire, SpinLockRelease, and ConditionVariable::wakeup.

Referenced by BarrierArriveAndWait(), ConditionVariableTimedSleep(), InvalidateObsoleteReplicationSlots(), ReplicationSlotAcquireInternal(), and RequestCheckpoint().

59 {
60  int pgprocno = MyProc->pgprocno;
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  */
70  if (cv_sleep_target != NULL)
72 
73  /* Record the condition variable on which we will sleep. */
74  cv_sleep_target = cv;
75 
76  /* Add myself to the wait queue. */
77  SpinLockAcquire(&cv->mutex);
78  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
79  SpinLockRelease(&cv->mutex);
80 }
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
#define SpinLockAcquire(lock)
Definition: spin.h:62
void ConditionVariableCancelSleep(void)
#define SpinLockRelease(lock)
Definition: spin.h:64
static ConditionVariable * cv_sleep_target
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
int pgprocno
Definition: proc.h:141

◆ ConditionVariableSignal()

void ConditionVariableSignal ( ConditionVariable cv)

Definition at line 256 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(), _bt_parallel_scan_and_sort(), and ConditionVariableCancelSleep().

257 {
258  PGPROC *proc = NULL;
259 
260  /* Remove the first process from the wakeup queue (if any). */
261  SpinLockAcquire(&cv->mutex);
262  if (!proclist_is_empty(&cv->wakeup))
263  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
264  SpinLockRelease(&cv->mutex);
265 
266  /* If we found someone sleeping, set their latch to wake them up. */
267  if (proc != NULL)
268  SetLatch(&proc->procLatch);
269 }
proclist_head wakeup
void SetLatch(Latch *latch)
Definition: latch.c:505
Latch procLatch
Definition: proc.h:121
#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
static bool proclist_is_empty(proclist_head *list)
Definition: proclist.h:38
Definition: proc.h:112

◆ ConditionVariableSleep()

void ConditionVariableSleep ( ConditionVariable cv,
uint32  wait_event_info 
)

Definition at line 98 of file condition_variable.c.

References ConditionVariableTimedSleep().

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

99 {
100  (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
101  wait_event_info);
102 }
bool ConditionVariableTimedSleep(ConditionVariable *cv, long timeout, uint32 wait_event_info)

◆ ConditionVariableTimedSleep()

bool ConditionVariableTimedSleep ( ConditionVariable cv,
long  timeout,
uint32  wait_event_info 
)

Definition at line 112 of file condition_variable.c.

References Assert, CHECK_FOR_INTERRUPTS, ConditionVariablePrepareToSleep(), INSTR_TIME_GET_MILLISEC, INSTR_TIME_SET_CURRENT, INSTR_TIME_SUBTRACT, ConditionVariable::mutex, MyLatch, MyProc, PGPROC::pgprocno, proclist_contains, proclist_push_tail, ResetLatch(), SpinLockAcquire, SpinLockRelease, start_time, WaitLatch(), ConditionVariable::wakeup, WL_EXIT_ON_PM_DEATH, WL_LATCH_SET, and WL_TIMEOUT.

Referenced by ConditionVariableSleep(), and InvalidateObsoleteReplicationSlots().

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  {
147  INSTR_TIME_SET_CURRENT(start_time);
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 
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  */
185  SpinLockAcquire(&cv->mutex);
186  if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
187  {
188  done = true;
189  proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
190  }
191  SpinLockRelease(&cv->mutex);
192 
193  /* We were signaled, so return */
194  if (done)
195  return false;
196 
197  /* If we're not done, update cur_timeout for next iteration */
198  if (timeout >= 0)
199  {
200  INSTR_TIME_SET_CURRENT(cur_time);
201  INSTR_TIME_SUBTRACT(cur_time, start_time);
202  cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
203 
204  /* Have we crossed the timeout threshold? */
205  if (cur_timeout <= 0)
206  return true;
207  }
208  }
209 }
#define WL_TIMEOUT
Definition: latch.h:127
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:202
struct timeval instr_time
Definition: instr_time.h:150
void ResetLatch(Latch *latch)
Definition: latch.c:588
static time_t start_time
Definition: pg_ctl.c:99
int WaitLatch(Latch *latch, int wakeEvents, long timeout, uint32 wait_event_info)
Definition: latch.c:390
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
#define SpinLockAcquire(lock)
Definition: spin.h:62
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:170
#define SpinLockRelease(lock)
Definition: spin.h:64
static ConditionVariable * cv_sleep_target
#define Assert(condition)
Definition: c.h:746
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:191
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
int pgprocno
Definition: proc.h:141
struct Latch * MyLatch
Definition: globals.c:54
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
#define WL_LATCH_SET
Definition: latch.h:124
#define WL_EXIT_ON_PM_DEATH
Definition: latch.h:129
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:195

Variable Documentation

◆ cv_sleep_target

ConditionVariable* cv_sleep_target = NULL
static

Definition at line 31 of file condition_variable.c.

Referenced by ConditionVariableCancelSleep().