bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept { verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously #ifdef _WIN32 LARGE_INTEGER timeout; timeout.QuadPart = _timeout * -10; if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout)) { verify(HERE), rc == WAIT_TIMEOUT; // Retire if (!m_value.fetch_op([](u32& value) { if (value) value--; })) { NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr); return true; } return false; } return true; #elif __linux__ timespec timeout; timeout.tv_sec = _timeout / 1000000; timeout.tv_nsec = (_timeout % 1000000) * 1000; for (u32 value = _old + 1;; value = m_value) { const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0 ? 0 : errno; // Normal or timeout wakeup if (!err || (_timeout != -1 && err == ETIMEDOUT)) { // Cleanup (remove waiter) verify(HERE), m_value--; return !err; } // Not a wakeup verify(HERE), err == EAGAIN; } #else // TODO std::this_thread::sleep_for(std::chrono::microseconds(50)); verify(HERE), m_value--; return true; #endif }
void shared_mutex::imp_wait() { #ifdef _WIN32 NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr); #else while (true) { // Load new value, try to acquire c_sig auto [value, ok] = m_value.fetch_op([](u32& value) { if (value >= c_sig) { value -= c_sig; return true; } return false; }); if (ok) { return; } futex(reinterpret_cast<int*>(&m_value.raw()), FUTEX_WAIT_BITSET_PRIVATE, value, nullptr, nullptr, c_sig); } #endif }
/** * Waits for a wait block to be unblocked. * * \param WaitBlock A wait block. * \param Spin TRUE to spin, FALSE to block immediately. * \param Timeout A timeout value. */ __mayRaise FORCEINLINE NTSTATUS PhpBlockOnQueuedWaitBlock( __inout PPH_QUEUED_WAIT_BLOCK WaitBlock, __in BOOLEAN Spin, __in_opt PLARGE_INTEGER Timeout ) { NTSTATUS status; ULONG i; if (Spin) { PHLIB_INC_STATISTIC(QlBlockSpins); for (i = PhQueuedLockSpinCount; i != 0; i--) { if (!(*(volatile ULONG *)&WaitBlock->Flags & PH_QUEUED_WAITER_SPINNING)) return STATUS_SUCCESS; YieldProcessor(); } } if (_interlockedbittestandreset((PLONG)&WaitBlock->Flags, PH_QUEUED_WAITER_SPINNING_SHIFT)) { PHLIB_INC_STATISTIC(QlBlockWaits); status = NtWaitForKeyedEvent( PhQueuedLockKeyedEventHandle, WaitBlock, FALSE, Timeout ); // If an error occurred (timeout is not an error), raise an exception // as it is nearly impossible to recover from this situation. if (!NT_SUCCESS(status)) PhRaiseStatus(status); } else { status = STATUS_SUCCESS; } return status; }