/* * Like WaitLatch, but with an extra socket argument for WL_SOCKET_* * conditions. * * When waiting on a socket, EOF and error conditions are reported by * returning the socket as readable/writable or both, depending on * WL_SOCKET_READABLE/WL_SOCKET_WRITEABLE being specified. * * NB: These days this is just a wrapper around the WaitEventSet API. When * using a latch very frequently, consider creating a longer living * WaitEventSet instead; that's more efficient. */ int WaitLatchOrSocket(volatile Latch *latch, int wakeEvents, pgsocket sock, long timeout) { int ret = 0; int rc; WaitEvent event; WaitEventSet *set = CreateWaitEventSet(CurrentMemoryContext, 3); if (wakeEvents & WL_TIMEOUT) Assert(timeout >= 0); else timeout = -1; if (wakeEvents & WL_LATCH_SET) AddWaitEventToSet(set, WL_LATCH_SET, PGINVALID_SOCKET, (Latch *) latch, NULL); if (wakeEvents & WL_POSTMASTER_DEATH) AddWaitEventToSet(set, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, NULL, NULL); if (wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE)) { int ev; ev = wakeEvents & (WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE); AddWaitEventToSet(set, ev, sock, NULL, NULL); } rc = WaitEventSetWait(set, timeout, &event, 1); if (rc == 0) ret |= WL_TIMEOUT; else { ret |= event.events & (WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_SOCKET_WRITEABLE); } FreeWaitEventSet(set); return ret; }
/* * Wait for the given condition variable to be signaled. * * This should be called in a predicate loop that tests for a specific exit * condition and otherwise sleeps, like so: * * ConditionVariablePrepareToSleep(cv); // optional * while (condition for which we are waiting is not true) * ConditionVariableSleep(cv, wait_event_info); * ConditionVariableCancelSleep(); * * wait_event_info should be a value from one of the WaitEventXXX enums * defined in pgstat.h. This controls the contents of pg_stat_activity's * wait_event_type and wait_event columns while waiting. */ void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info) { WaitEvent event; bool done = false; /* * If the caller didn't prepare to sleep explicitly, then do so now and * return immediately. The caller's predicate loop should immediately * call again if its exit condition is not yet met. This will result in * the exit condition being tested twice before we first sleep. The extra * test can be prevented by calling ConditionVariablePrepareToSleep(cv) * first. Whether it's worth doing that depends on whether you expect the * exit condition to be met initially, in which case skipping the prepare * is recommended because it avoids manipulations of the wait list, or not * met initially, in which case preparing first is better because it * avoids one extra test of the exit condition. * * If we are currently prepared to sleep on some other CV, we just cancel * that and prepare this one; see ConditionVariablePrepareToSleep. */ if (cv_sleep_target != cv) { ConditionVariablePrepareToSleep(cv); return; } do { CHECK_FOR_INTERRUPTS(); /* * Wait for latch to be set. (If we're awakened for some other * reason, the code below will cope anyway.) */ (void) WaitEventSetWait(cv_wait_event_set, -1, &event, 1, wait_event_info); /* Reset latch before examining the state of the wait list. */ ResetLatch(MyLatch); /* * If this process has been taken out of the wait list, then we know * that it has been signaled by ConditionVariableSignal (or * ConditionVariableBroadcast), so we should return to the caller. But * that doesn't guarantee that the exit condition is met, only that we * ought to check it. So we must put the process back into the wait * list, to ensure we don't miss any additional wakeup occurring while * the caller checks its exit condition. We can take ourselves out of * the wait list only when the caller calls * ConditionVariableCancelSleep. * * If we're still in the wait list, then the latch must have been set * by something other than ConditionVariableSignal; though we don't * guarantee not to return spuriously, we'll avoid this obvious case. */ SpinLockAcquire(&cv->mutex); if (!proclist_contains(&cv->wakeup, MyProc->pgprocno, cvWaitLink)) { done = true; proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink); } SpinLockRelease(&cv->mutex); } while (!done); }