/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuConditionEx.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "AuConditionEx.hpp" #include "AuSemaphore.Generic.hpp" #include "SMTYield.hpp" // for: while_bc namespace Aurora::Threading::Primitives { struct SemaphoreConditionVariableImpl : IFlexibleConditionVariable { SemaphoreConditionVariableImpl(); void WaitForSignal(IWaitable *pWaitable) override; void WaitForSignal() override; bool WaitForSignalNS(AuUInt64 uRelativeNanoseconds) override; bool WaitForSignalNS(IWaitable *pWaitable, AuUInt64 uRelativeNanoseconds) override; bool WaitForSignalAbsNS(AuUInt64 uAbsNanoseconds) override; bool WaitForSignalAbsNS(IWaitable *pWaitable, AuUInt64 uAbsNanoseconds) override; void Signal() override; void Broadcast() override; private: SemaphoreImpl s_; AuAUInt32 uWaiters_; }; SemaphoreConditionVariableImpl::SemaphoreConditionVariableImpl() : s_(0), uWaiters_(0) { } bool SemaphoreConditionVariableImpl::WaitForSignalNS(AuUInt64 uRelativeNanoseconds) { return WaitForSignalNS(nullptr, uRelativeNanoseconds); } bool SemaphoreConditionVariableImpl::WaitForSignalAbsNS(IWaitable *pWaitable, AuUInt64 uNanoseconds) { AuAtomicAdd(&this->uWaiters_, 1u); if (pWaitable) { pWaitable->Unlock(); } auto bSuccess = this->s_.LockAbsNS(uNanoseconds); if (!bSuccess) { auto uWaiters = this->uWaiters_; auto uWaitCount = 1; while (uWaiters && AuAtomicCompareExchange(&this->uWaiters_, uWaiters - uWaitCount, uWaiters) != uWaiters) { uWaiters = this->uWaiters_; if (uWaiters == 0) { break; } } } if (pWaitable) { pWaitable->Lock(); } return bSuccess; } bool SemaphoreConditionVariableImpl::WaitForSignalNS(IWaitable *pWaitable, AuUInt64 uAbsNanoseconds) { return this->WaitForSignalAbsNS(pWaitable, uAbsNanoseconds ? AuTime::SteadyClockNS() + uAbsNanoseconds : 0); } void SemaphoreConditionVariableImpl::WaitForSignal(IWaitable *pWaitable) { AuAtomicAdd(&this->uWaiters_, 1u); if (pWaitable) { pWaitable->Unlock(); } this->s_.Lock(); if (pWaitable) { pWaitable->Lock(); } } void SemaphoreConditionVariableImpl::WaitForSignal() { WaitForSignal(nullptr); } bool SemaphoreConditionVariableImpl::WaitForSignalAbsNS(AuUInt64 uAbsNanoseconds) { return this->WaitForSignalAbsNS({}, uAbsNanoseconds); } void SemaphoreConditionVariableImpl::Signal() { AuUInt32 uWaitCount {}; AuUInt32 uWaiters {}; uWaiters = AuAtomicLoad(&this->uWaiters_); if (uWaiters == 0) { return; } this->s_.Unlock(); while (uWaiters && AuAtomicCompareExchange(&this->uWaiters_, uWaiters - 1u, uWaiters) != uWaiters) { uWaiters = this->uWaiters_; if (uWaiters == 0) { return; } } } void SemaphoreConditionVariableImpl::Broadcast() { AuUInt32 uWaitCount {}; AuUInt32 uWaiters {}; while_bc ((uWaiters = AuAtomicLoad(&this->uWaiters_))) { this->s_.Unlock(uWaiters); uWaitCount = uWaiters; while (uWaiters && AuAtomicCompareExchange(&this->uWaiters_, uWaiters - uWaitCount, uWaiters) != uWaiters) { uWaiters = this->uWaiters_; if (uWaiters <= uWaitCount) { uWaitCount = uWaiters; } } } } AUKN_SYM IFlexibleConditionVariable *FlexibleConditionVariableNew() { return _new SemaphoreConditionVariableImpl(); } AUKN_SYM void FlexibleConditionVariableRelease(IFlexibleConditionVariable *pCVEx) { AuSafeDelete(pCVEx); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, FlexibleConditionVariable, SemaphoreConditionVariableImpl) }