AuroraRuntime/Source/Threading/Primitives/AuConditionVariable.Generic.cpp
2024-04-13 22:49:05 +01:00

184 lines
4.5 KiB
C++

/***
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 <Source/RuntimeInternal.hpp>
#include <Source/Threading/AuWaitFor.hpp>
#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 DoTryIf([=]()
{
return this->TryTakeOneNoSpin();
});
}
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &pMutex)
{
return _new ConditionVariableGenericImpl(pMutex);
}
AUKN_SYM void ConditionVariableRelease(IConditionVariable *pCV)
{
AuSafeDelete<ConditionVariableGenericImpl *>(pCV);
}
AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableGenericImpl, (const AuSPtr<IConditionMutex> &, pMutex))
}
#endif