/*** Copyright (C) 2021-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuConditionVariable.Generic.cpp Date: 2021-6-14 Author: Reece ***/ #include #include #include "AuConditionVariable.Generic.hpp" #include "../AuWakeInternal.hpp" #include "SMTYield.hpp" #if defined(_AURUNTIME_GENERICCV) namespace Aurora::Threading::Primitives { ConditionVariableGeneric::ConditionVariableGeneric() { } ConditionVariableGeneric::~ConditionVariableGeneric() { } bool ConditionVariableGeneric::WaitForSignalNsEx(GenericConditionMutex *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 ConditionVariableGeneric::Signal() { AuUInt32 uWaiters {}; uWaiters = AuAtomicLoad(&this->uSleeping_); if (uWaiters == 0) { return; } AuAtomicAdd(&this->uState_, 1u); InternalLTSWakeOne(&this->uState_); while (uWaiters && AuAtomicCompareExchange(&this->uSleeping_, uWaiters - 1u, uWaiters) != uWaiters) { uWaiters = this->uSleeping_; if (uWaiters == 0) { return; } } } void ConditionVariableGeneric::Broadcast() { AuUInt32 uWaitCount {}; AuUInt32 uWaiters {}; while_bc (uWaiters = AuAtomicLoad(&this->uSleeping_)) { AuAtomicAdd(&this->uState_, uWaiters); InternalLTSWakeCount(&this->uState_, uWaiters); uWaitCount = uWaiters; while (uWaiters && AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters) { uWaiters = AuAtomicLoad(&this->uSleeping_); if (uWaiters <= uWaitCount) { uWaitCount = uWaiters; } } } } bool ConditionVariableGeneric::WaitOne(AuUInt64 qwTimeoutRelative, bool bSpin) { AuUInt64 uStart {}; AuUInt64 uEnd {}; if (qwTimeoutRelative != 0) { uStart = AuTime::SteadyClockNS(); uEnd = uStart + qwTimeoutRelative; } if (bSpin ? this->TryTakeOneSpin() : this->TryTakeOneNoSpin()) { return true; } auto old = this->uState_; while (!((old != 0) && (AuAtomicCompareExchange(&this->uState_, old - 1, old) == old))) { static const AuUInt32 kRef { 0 }; if (qwTimeoutRelative != 0) { if (!InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEnd)) { return false; } } else { (void)InternalLTSWaitOnAddressHighRes(&this->uState_, &kRef, 4, uEnd); } old = this->uState_; } return true; } bool ConditionVariableGeneric::TryTakeOneNoSpin() { auto old = this->uState_; return old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old; } bool ConditionVariableGeneric::TryTakeOneSpin() { if (ThrdCfg::gPreferUnixPrimitivesNoSpin) { return this->TryTakeOneNoSpin(); } return DoTryIfAlderLake([=]() { return this->TryTakeOneNoSpin(); }, &this->uState_); } AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr &pMutex) { return _new ConditionVariableGenericImpl(pMutex); } AUKN_SYM void ConditionVariableRelease(IConditionVariable *pCV) { AuSafeDelete(pCV); } AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableGenericImpl, (const AuSPtr &, pMutex)) } #endif