/*** 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 "AuMutex.Generic.hpp" #include "AuSemaphore.Generic.hpp" namespace Aurora::Threading::Primitives { // Implementing Condition Variables with Semaphores, Andrew Birrell, MSFT // Subsitute semaphore count = 1 with a spinlock and... struct SemaphoreConditionVariableImpl : ConditionEx { SemaphoreConditionVariableImpl(); void WaitForSignal(Aurora::Threading::IWaitable *pWaitable) override; void WaitForSignal(const AuSPtr &pWaitable) override; void WaitForSignal() override; void Signal() override; void Broadcast() override; private: std::atomic waiters_; Semaphore s_, h_; SpinLock x_; }; SemaphoreConditionVariableImpl::SemaphoreConditionVariableImpl() : s_(0), h_(0) { } void SemaphoreConditionVariableImpl::WaitForSignal(Aurora::Threading::IWaitable *pWaitable) { return WaitForSignal(AuUnsafeRaiiToShared(pWaitable)); } void SemaphoreConditionVariableImpl::WaitForSignal(const AuSPtr &pWaitable) { this->x_.Lock(); this->waiters_++; this->x_.Unlock(); if (pWaitable) { pWaitable->Unlock(); } this->s_.Lock(); this->h_.Unlock(); if (pWaitable) { pWaitable->Lock(); } } void SemaphoreConditionVariableImpl::WaitForSignal() { WaitForSignal(nullptr); } void SemaphoreConditionVariableImpl::Signal() { this->x_.Lock(); if (this->waiters_ > 0) { this->waiters_--; this->s_.Unlock(); // 1 may pass this->h_.Lock(); // finish operation - wait for the alive signal back } this->x_.Unlock(); } void SemaphoreConditionVariableImpl::Broadcast() { this->x_.Lock(); this->s_.Unlock(this->waiters_); // all may pass at once for (auto i = 0; i < this->waiters_; i++) { this->h_.Lock(); // finish operation - wait for the alive signal back } this->waiters_ = 0; this->x_.Unlock(); } AUKN_SYM ConditionEx *FeaturefulConditionNew() { return _new SemaphoreConditionVariableImpl(); } AUKN_SYM void FeaturefulConditionRelease(ConditionEx *pCVEx) { AuSafeDelete(pCVEx); } }