/*** Copyright (C) 2023-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuConditionVariable.Linux.cpp Date: 2023-8-11 Author: Reece ***/ #include #include "AuConditionVariable.Generic.hpp" #include #include "SMTYield.hpp" #if !defined(_AURUNTIME_GENERICCV) namespace Aurora::Threading::Primitives { ConditionVariableLinux::ConditionVariableLinux() { } ConditionVariableLinux::~ConditionVariableLinux() { } bool ConditionVariableLinux::TryTakeOneNoSpin() { auto old = this->uState_; return old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old; } bool ConditionVariableLinux::TryTakeOneSpin() { if (this->TryTakeOneNoSpin()) { return true; } if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin) { return false; } return DoTryIfAlderLake([=]() { return this->TryTakeOneNoSpin(); }, &this->uState_); } bool ConditionVariableLinux::WaitOne(AuUInt64 qwTimeoutRelative, bool bSpin) { AuUInt64 uStart {}; AuUInt64 uEnd {}; struct timespec tspec; if (qwTimeoutRelative != 0) { uStart = AuTime::SteadyClockNS(); uEnd = uStart + qwTimeoutRelative; Time::monoabsns2ts(&tspec, uEnd); } if (bSpin ? this->TryTakeOneSpin() : this->TryTakeOneNoSpin()) { return true; } auto old = this->uState_; while (!((old != 0) && (AuAtomicCompareExchange(&this->uState_, old - 1, old) == old))) { if (qwTimeoutRelative != 0) { if (Time::SteadyClockNS() >= uEnd) { return false; } int ret {}; do { ret = futex_wait(&this->uState_, 0, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->uState_, 0)) == 0) { bStatus = true; continue; } if (ret == -EAGAIN) { bStatus = true; continue; } } while (ret == -EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Condvar wait failed: {}", ret) } old = this->uState_; } return true; } bool ConditionVariableLinux::WaitForSignalNsEx(LinuxConditionMutex *pMutex, AuUInt64 qwTimeoutRelative, bool bSpin) { AuAtomicAdd(&this->uSleeping_, 1u); if (pMutex) { pMutex->Unlock(); } auto bSuccess = this->WaitOne(qwTimeoutRelative, bSpin); if (!bSuccess) { auto uWaiters = this->uSleeping_; auto uWaitCount = 1; while (uWaiters && AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) { uWaiters = this->uSleeping_; if (uWaiters == 0) { break; } } } if (pMutex) { pMutex->Lock(); } return bSuccess; } void ConditionVariableLinux::Signal() { AuUInt32 uWaiters {}; uWaiters = AuAtomicLoad(&this->uSleeping_); if (uWaiters == 0) { return; } AuAtomicAdd(&this->uState_, 1u); futex_wake(&this->uState_, 1u); while (uWaiters && AuAtomicCompareExchange(&this->uSleeping_, uWaiters - 1u, uWaiters) != uWaiters) { uWaiters = this->uSleeping_; if (uWaiters == 0) { return; } } } void ConditionVariableLinux::Broadcast() { AuUInt32 uWaitCount {}; AuUInt32 uWaiters {}; while_bc ((uWaiters = AuAtomicLoad(&this->uSleeping_))) { AuAtomicAdd(&this->uState_, uWaiters); futex_wake(&this->uState_, uWaiters); uWaitCount = uWaiters; while (uWaiters && AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) { uWaiters = AuAtomicLoad(&this->uSleeping_); if (uWaiters <= uWaitCount) { uWaitCount = uWaiters; } } } } AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr &mutex) { return _new ConditionVariableImpl(mutex); } AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex) { AuSafeDelete(mutex); } AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableImpl, (const AuSPtr &, pMutex)) } #endif