|
| 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-2016, 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 | +staticConditionVariable*cv_sleep_target=NULL; |
| 31 | + |
| 32 | +/* Reusable WaitEventSet. */ |
| 33 | +staticWaitEventSet*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. 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 |
| 53 | +ConditionVariablePrepareToSleep(ConditionVariable*cv) |
| 54 | +{ |
| 55 | +intpgprocno=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); |
| 70 | +AddWaitEventToSet(cv_wait_event_set,WL_LATCH_SET,PGINVALID_SOCKET, |
| 71 | +&MyProc->procLatch,NULL); |
| 72 | +} |
| 73 | + |
| 74 | +/* Add myself to the wait queue. */ |
| 75 | +SpinLockAcquire(&cv->mutex); |
| 76 | +if (!proclist_contains(&cv->wakeup,pgprocno,cvWaitLink)) |
| 77 | +proclist_push_tail(&cv->wakeup,pgprocno,cvWaitLink); |
| 78 | +SpinLockRelease(&cv->mutex); |
| 79 | + |
| 80 | +/* Reset my latch before entering the caller's predicate loop. */ |
| 81 | +ResetLatch(&MyProc->procLatch); |
| 82 | +} |
| 83 | + |
| 84 | +/*-------------------------------------------------------------------------- |
| 85 | + * Wait for the given condition variable to be signaled. This should be |
| 86 | + * called in a predicate loop that tests for a specfic exit condition and |
| 87 | + * otherwise sleeps, like so: |
| 88 | + * |
| 89 | + * ConditionVariablePrepareToSleep(cv); [optional] |
| 90 | + * while (condition for which we are waiting is not true) |
| 91 | + * ConditionVariableSleep(cv, wait_event_info); |
| 92 | + * ConditionVariableCancelSleep(); |
| 93 | + * |
| 94 | + * Supply a value from one of the WaitEventXXX enums defined in pgstat.h to |
| 95 | + * control the contents of pg_stat_activity's wait_event_type and wait_event |
| 96 | + * columns while waiting. |
| 97 | + *-------------------------------------------------------------------------*/ |
| 98 | +void |
| 99 | +ConditionVariableSleep(ConditionVariable*cv,uint32wait_event_info) |
| 100 | +{ |
| 101 | +WaitEventevent; |
| 102 | +booldone= false; |
| 103 | + |
| 104 | +/* |
| 105 | + * If the caller didn't prepare to sleep explicitly, then do so now and |
| 106 | + * return immediately. The caller's predicate loop should immediately |
| 107 | + * call again if its exit condition is not yet met. This initial spurious |
| 108 | + * return can be avoided by calling ConditionVariablePrepareToSleep(cv) |
| 109 | + * first. Whether it's worth doing that depends on whether you expect the |
| 110 | + * condition to be met initially, in which case skipping the prepare |
| 111 | + * allows you to skip manipulation of the wait list, or not met intiailly, |
| 112 | + * in which case preparing first allows you to skip a spurious test of the |
| 113 | + * caller's exit condition. |
| 114 | + */ |
| 115 | +if (cv_sleep_target==NULL) |
| 116 | +{ |
| 117 | +ConditionVariablePrepareToSleep(cv); |
| 118 | +return; |
| 119 | +} |
| 120 | + |
| 121 | +/* Any earlier condition variable sleep must have been canceled. */ |
| 122 | +Assert(cv_sleep_target==cv); |
| 123 | + |
| 124 | +while (!done) |
| 125 | +{ |
| 126 | +CHECK_FOR_INTERRUPTS(); |
| 127 | + |
| 128 | +/* |
| 129 | + * Wait for latch to be set. We don't care about the result because |
| 130 | + * our contract permits spurious returns. |
| 131 | + */ |
| 132 | +WaitEventSetWait(cv_wait_event_set,-1,&event,1,wait_event_info); |
| 133 | + |
| 134 | +/* Reset latch before testing whether we can return. */ |
| 135 | +ResetLatch(&MyProc->procLatch); |
| 136 | + |
| 137 | +/* |
| 138 | + * If this process has been taken out of the wait list, then we know |
| 139 | + * that is has been signaled by ConditionVariableSignal. We put it |
| 140 | + * back into the wait list, so we don't miss any further signals while |
| 141 | + * the caller's loop checks its condition. If it hasn't been taken |
| 142 | + * out of the wait list, then the latch must have been set by |
| 143 | + * something other than ConditionVariableSignal; though we don't |
| 144 | + * guarantee not to return spuriously, we'll avoid these obvious |
| 145 | + * cases. |
| 146 | + */ |
| 147 | +SpinLockAcquire(&cv->mutex); |
| 148 | +if (!proclist_contains(&cv->wakeup,MyProc->pgprocno,cvWaitLink)) |
| 149 | +{ |
| 150 | +done= true; |
| 151 | +proclist_push_tail(&cv->wakeup,MyProc->pgprocno,cvWaitLink); |
| 152 | +} |
| 153 | +SpinLockRelease(&cv->mutex); |
| 154 | +} |
| 155 | +} |
| 156 | + |
| 157 | +/* |
| 158 | + * Cancel any pending sleep operation. We just need to remove ourselves |
| 159 | + * from the wait queue of any condition variable for which we have previously |
| 160 | + * prepared a sleep. |
| 161 | + */ |
| 162 | +void |
| 163 | +ConditionVariableCancelSleep(void) |
| 164 | +{ |
| 165 | +ConditionVariable*cv=cv_sleep_target; |
| 166 | + |
| 167 | +if (cv==NULL) |
| 168 | +return; |
| 169 | + |
| 170 | +SpinLockAcquire(&cv->mutex); |
| 171 | +if (proclist_contains(&cv->wakeup,MyProc->pgprocno,cvWaitLink)) |
| 172 | +proclist_delete(&cv->wakeup,MyProc->pgprocno,cvWaitLink); |
| 173 | +SpinLockRelease(&cv->mutex); |
| 174 | + |
| 175 | +cv_sleep_target=NULL; |
| 176 | +} |
| 177 | + |
| 178 | +/* |
| 179 | + * Wake up one sleeping process, assuming there is at least one. |
| 180 | + * |
| 181 | + * The return value indicates whether or not we woke somebody up. |
| 182 | + */ |
| 183 | +bool |
| 184 | +ConditionVariableSignal(ConditionVariable*cv) |
| 185 | +{ |
| 186 | +PGPROC*proc=NULL; |
| 187 | + |
| 188 | +/* Remove the first process from the wakeup queue (if any). */ |
| 189 | +SpinLockAcquire(&cv->mutex); |
| 190 | +if (!proclist_is_empty(&cv->wakeup)) |
| 191 | +proc=proclist_pop_head_node(&cv->wakeup,cvWaitLink); |
| 192 | +SpinLockRelease(&cv->mutex); |
| 193 | + |
| 194 | +/* If we found someone sleeping, set their latch to wake them up. */ |
| 195 | +if (proc!=NULL) |
| 196 | +{ |
| 197 | +SetLatch(&proc->procLatch); |
| 198 | +return true; |
| 199 | +} |
| 200 | + |
| 201 | +/* No sleeping processes. */ |
| 202 | +return false; |
| 203 | +} |
| 204 | + |
| 205 | +/* |
| 206 | + * Wake up all sleeping processes. |
| 207 | + * |
| 208 | + * The return value indicates the number of processes we woke. |
| 209 | + */ |
| 210 | +int |
| 211 | +ConditionVariableBroadcast(ConditionVariable*cv) |
| 212 | +{ |
| 213 | +intnwoken=0; |
| 214 | + |
| 215 | +/* |
| 216 | + * Let's just do this the dumbest way possible. We could try to dequeue |
| 217 | + * all the sleepers at once to save spinlock cycles, but it's a bit hard |
| 218 | + * to get that right in the face of possible sleep cancelations, and |
| 219 | + * we don't want to loop holding the mutex. |
| 220 | + */ |
| 221 | +while (ConditionVariableSignal(cv)) |
| 222 | +++nwoken; |
| 223 | + |
| 224 | +returnnwoken; |
| 225 | +} |