PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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"
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. */
31 
32 /* Reusable WaitEventSet. */
34 
35 /*
36  * Initialize a condition variable.
37  */
38 void
40 {
41  SpinLockInit(&cv->mutex);
42  proclist_init(&cv->wakeup);
43 }
44 
45 /*
46  * Prepare to wait on a given condition variable. This can optionally be
47  * called before entering a test/sleep loop. Alternatively, the call to
48  * ConditionVariablePrepareToSleep can be omitted. The only advantage of
49  * calling ConditionVariablePrepareToSleep is that it avoids an initial
50  * double-test of the user's predicate in the case that we need to wait.
51  */
52 void
54 {
55  int pgprocno = MyProc->pgprocno;
56 
57  /*
58  * It's not legal to prepare a sleep until the previous sleep has been
59  * completed or canceled.
60  */
61  Assert(cv_sleep_target == NULL);
62 
63  /* Record the condition variable on which we will sleep. */
64  cv_sleep_target = cv;
65 
66  /* Create a reusable WaitEventSet. */
67  if (cv_wait_event_set == NULL)
68  {
69  cv_wait_event_set = CreateWaitEventSet(TopMemoryContext, 1);
71  &MyProc->procLatch, NULL);
72  }
73 
74  /*
75  * Reset my latch before adding myself to the queue and before entering
76  * the caller's predicate loop.
77  */
79 
80  /* Add myself to the wait queue. */
81  SpinLockAcquire(&cv->mutex);
82  if (!proclist_contains(&cv->wakeup, pgprocno, cvWaitLink))
83  proclist_push_tail(&cv->wakeup, pgprocno, cvWaitLink);
84  SpinLockRelease(&cv->mutex);
85 }
86 
87 /*--------------------------------------------------------------------------
88  * Wait for the given condition variable to be signaled. This should be
89  * called in a predicate loop that tests for a specific exit condition and
90  * otherwise sleeps, like so:
91  *
92  * ConditionVariablePrepareToSleep(cv); [optional]
93  * while (condition for which we are waiting is not true)
94  * ConditionVariableSleep(cv, wait_event_info);
95  * ConditionVariableCancelSleep();
96  *
97  * Supply a value from one of the WaitEventXXX enums defined in pgstat.h to
98  * control the contents of pg_stat_activity's wait_event_type and wait_event
99  * columns while waiting.
100  *-------------------------------------------------------------------------*/
101 void
103 {
104  WaitEvent event;
105  bool done = false;
106 
107  /*
108  * If the caller didn't prepare to sleep explicitly, then do so now and
109  * return immediately. The caller's predicate loop should immediately
110  * call again if its exit condition is not yet met. This initial spurious
111  * return can be avoided by calling ConditionVariablePrepareToSleep(cv)
112  * first. Whether it's worth doing that depends on whether you expect the
113  * condition to be met initially, in which case skipping the prepare
114  * allows you to skip manipulation of the wait list, or not met initially,
115  * in which case preparing first allows you to skip a spurious test of the
116  * caller's exit condition.
117  */
118  if (cv_sleep_target == NULL)
119  {
121  return;
122  }
123 
124  /* Any earlier condition variable sleep must have been canceled. */
125  Assert(cv_sleep_target == cv);
126 
127  while (!done)
128  {
130 
131  /*
132  * Wait for latch to be set. We don't care about the result because
133  * our contract permits spurious returns.
134  */
135  WaitEventSetWait(cv_wait_event_set, -1, &event, 1, wait_event_info);
136 
137  /* Reset latch before testing whether we can return. */
139 
140  /*
141  * If this process has been taken out of the wait list, then we know
142  * that is has been signaled by ConditionVariableSignal. We put it
143  * back into the wait list, so we don't miss any further signals while
144  * the caller's loop checks its condition. If it hasn't been taken
145  * out of the wait list, then the latch must have been set by
146  * something other than ConditionVariableSignal; though we don't
147  * guarantee not to return spuriously, we'll avoid these obvious
148  * cases.
149  */
150  SpinLockAcquire(&cv->mutex);
151  if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
152  {
153  done = true;
154  proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
155  }
156  SpinLockRelease(&cv->mutex);
157  }
158 }
159 
160 /*
161  * Cancel any pending sleep operation. We just need to remove ourselves
162  * from the wait queue of any condition variable for which we have previously
163  * prepared a sleep.
164  */
165 void
167 {
169 
170  if (cv == NULL)
171  return;
172 
173  SpinLockAcquire(&cv->mutex);
174  if (proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink))
175  proclist_delete(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
176  SpinLockRelease(&cv->mutex);
177 
178  cv_sleep_target = NULL;
179 }
180 
181 /*
182  * Wake up one sleeping process, assuming there is at least one.
183  *
184  * The return value indicates whether or not we woke somebody up.
185  */
186 bool
188 {
189  PGPROC *proc = NULL;
190 
191  /* Remove the first process from the wakeup queue (if any). */
192  SpinLockAcquire(&cv->mutex);
193  if (!proclist_is_empty(&cv->wakeup))
194  proc = proclist_pop_head_node(&cv->wakeup, cvWaitLink);
195  SpinLockRelease(&cv->mutex);
196 
197  /* If we found someone sleeping, set their latch to wake them up. */
198  if (proc != NULL)
199  {
200  SetLatch(&proc->procLatch);
201  return true;
202  }
203 
204  /* No sleeping processes. */
205  return false;
206 }
207 
208 /*
209  * Wake up all sleeping processes.
210  *
211  * The return value indicates the number of processes we woke.
212  */
213 int
215 {
216  int nwoken = 0;
217 
218  /*
219  * Let's just do this the dumbest way possible. We could try to dequeue
220  * all the sleepers at once to save spinlock cycles, but it's a bit hard
221  * to get that right in the face of possible sleep cancelations, and
222  * we don't want to loop holding the mutex.
223  */
224  while (ConditionVariableSignal(cv))
225  ++nwoken;
226 
227  return nwoken;
228 }
int AddWaitEventToSet(WaitEventSet *set, uint32 events, pgsocket fd, Latch *latch, void *user_data)
Definition: latch.c:612
bool ConditionVariableSignal(ConditionVariable *cv)
proclist_head wakeup
PGPROC * MyProc
Definition: proc.c:67
#define SpinLockInit(lock)
Definition: spin.h:60
static WaitEventSet * cv_wait_event_set
int ConditionVariableBroadcast(ConditionVariable *cv)
void ResetLatch(volatile Latch *latch)
Definition: latch.c:461
#define proclist_delete(list, procno, link_member)
Definition: proclist.h:176
Latch procLatch
Definition: proc.h:93
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
WaitEventSet * CreateWaitEventSet(MemoryContext context, int nevents)
Definition: latch.c:484
#define SpinLockAcquire(lock)
Definition: spin.h:62
void ConditionVariableInit(ConditionVariable *cv)
void ConditionVariableCancelSleep(void)
unsigned int uint32
Definition: c.h:265
MemoryContext TopMemoryContext
Definition: mcxt.c:43
#define SpinLockRelease(lock)
Definition: spin.h:64
#define PGINVALID_SOCKET
Definition: port.h:24
static ConditionVariable * cv_sleep_target
#define proclist_pop_head_node(list, link_member)
Definition: proclist.h:182
void SetLatch(volatile Latch *latch)
Definition: latch.c:379
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
#define NULL
Definition: c.h:226
#define Assert(condition)
Definition: c.h:671
static bool proclist_is_empty(proclist_head *list)
Definition: proclist.h:38
#define proclist_push_tail(list, procno, link_member)
Definition: proclist.h:180
int pgprocno
Definition: proc.h:99
static void proclist_init(proclist_head *list)
Definition: proclist.h:29
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:97
Definition: proc.h:84
#define WL_LATCH_SET
Definition: latch.h:124
int WaitEventSetWait(WaitEventSet *set, long timeout, WaitEvent *occurred_events, int nevents, uint32 wait_event_info)
Definition: latch.c:870
#define proclist_contains(list, procno, link_member)
Definition: proclist.h:184