/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: ConditionEx.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "ConditionEx.hpp" #include "Mutex.Generic.hpp" #include "Semaphore.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 *waitable) override; void WaitForSignal(const AuSPtr &waitable) 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 *waitable) { return WaitForSignal(AuUnsafeRaiiToShared(waitable)); } void SemaphoreConditionVariableImpl::WaitForSignal(const AuSPtr &waitable) { x_.Lock(); waiters_++; x_.Unlock(); if (waitable) { waitable->Unlock(); } s_.Lock(); h_.Unlock(); if (waitable) { waitable->Lock(); } } void SemaphoreConditionVariableImpl::WaitForSignal() { WaitForSignal(nullptr); } void SemaphoreConditionVariableImpl::Signal() { x_.Lock(); if (waiters_ > 0) { waiters_--; s_.Unlock(); // 1 may pass h_.Lock(); // finish operation - wait for the alive signal back } x_.Unlock(); } void SemaphoreConditionVariableImpl::Broadcast() { x_.Lock(); s_.Unlock(waiters_); // all may pass at once for (auto i = 0; i < waiters_; i++) { h_.Lock(); // finish operation - wait for the alive signal back } waiters_ = 0; x_.Unlock(); } AUKN_SYM ConditionEx *FeaturefulConditionNew() { return _new SemaphoreConditionVariableImpl(); } AUKN_SYM void FeaturefulConditionRelease(ConditionEx *semaphore) { AuSafeDelete(semaphore); } }