/*** Copyright (C) 2021 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" #if defined(_AURUNTIME_GENERICCV) namespace Aurora::Threading::Primitives { ConditionVariableImpl::ConditionVariableImpl(const AuSPtr &mutex) : mutex_(AuDynamicCast(mutex)) { } AuSPtr ConditionVariableImpl::GetMutex() { return mutex_; } bool ConditionVariableImpl::WaitForSignal(AuUInt32 timeout) { // yield then wake up spuriously - this is should be illegal, but works well enough as a place holder mutex_->Unlock(); for (int i = 0; i < 20; i++) { YieldToOtherThread(); } mutex_->Lock(); /** TODO: A more technically competent solution may be more sophisticated than anything proposed here, but the following seems good enough. It's same thing without the spurious wake ups and hopefully scalable timeout-relative yielding wait -> waiting_++ WaitFor([]() { auto old = signals_.load(std::memory_order_relaxed); return old != 0 && signals_.compare_exchange_strong(old, old - 1); }) */ return true; } void ConditionVariableImpl::Signal() { signals_++; waiting_--; } void ConditionVariableImpl::Broadcast() { auto initwaiting = waiting_.load(std::memory_order_relaxed); if (initwaiting < 0) return; signals_ += initwaiting; #if 0 auto old = initwaiting; if (!old) return; while (!(waiting_.compare_exchange_strong(old, old - initwaiting))) { old = waiting_.load(std::memory_order_relaxed); if (!old) return; } #else waiting_ -= initwaiting; // is it possible to go negative on waiters? yes! // is it a big deal? well, no actually // the only way this can happen is if we have above 0 signals and no threads waiting // it should sort itself out #endif } AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr &mutex) { return _new ConditionVariableImpl(mutex); } AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex) { AuSafeDelete(mutex); } } #endif