AuroraRuntime/Source/Threading/Primitives/AuConditionVariable.Linux.cpp

220 lines
5.4 KiB
C++

/***
Copyright (C) 2023-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionVariable.Linux.cpp
Date: 2023-8-11
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionVariable.Generic.hpp"
#include <Source/Time/Time.hpp>
#include "SMTYield.hpp"
#if !defined(_AURUNTIME_GENERICCV)
namespace Aurora::Threading::Primitives
{
ConditionVariableLinux::ConditionVariableLinux()
{
}
ConditionVariableLinux::~ConditionVariableLinux()
{
}
bool ConditionVariableLinux::TryTakeOneNoSpin()
{
auto old = this->uState_;
return old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old;
}
bool ConditionVariableLinux::TryTakeOneSpin()
{
if (this->TryTakeOneNoSpin())
{
return true;
}
if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin)
{
return false;
}
return DoTryIfAlderLake([=]()
{
return this->TryTakeOneNoSpin();
}, &this->uState_);
}
bool ConditionVariableLinux::WaitOne(AuUInt64 qwTimeoutRelative,
bool bSpin)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
struct timespec tspec;
if (qwTimeoutRelative != 0)
{
uStart = AuTime::SteadyClockNS();
uEnd = uStart + qwTimeoutRelative;
Time::monoabsns2ts(&tspec, uEnd);
}
if (bSpin ? this->TryTakeOneSpin() : this->TryTakeOneNoSpin())
{
return true;
}
auto old = this->uState_;
while (!((old != 0) &&
(AuAtomicCompareExchange(&this->uState_, old - 1, old) == old)))
{
if (qwTimeoutRelative != 0)
{
if (Time::SteadyClockNS() >= uEnd)
{
return false;
}
int ret {};
do
{
ret = futex_wait(&this->uState_, 0, &tspec);
}
while (ret == -EINTR);
}
else
{
int ret {};
bool bStatus {};
do
{
if ((ret = futex_wait(&this->uState_, 0)) == 0)
{
bStatus = true;
continue;
}
if (ret == -EAGAIN)
{
bStatus = true;
continue;
}
}
while (ret == -EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Condvar wait failed: {}", ret)
}
old = this->uState_;
}
return true;
}
bool ConditionVariableLinux::WaitForSignalNsEx(LinuxConditionMutex *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 ConditionVariableLinux::Signal()
{
AuUInt32 uWaiters {};
uWaiters = AuAtomicLoad(&this->uSleeping_);
if (uWaiters == 0)
{
return;
}
AuAtomicAdd(&this->uState_, 1u);
futex_wake(&this->uState_, 1u);
while (uWaiters &&
AuAtomicCompareExchange(&this->uSleeping_, uWaiters - 1u, uWaiters) != uWaiters)
{
uWaiters = this->uSleeping_;
if (uWaiters == 0)
{
return;
}
}
}
void ConditionVariableLinux::Broadcast()
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
while_bc ((uWaiters = AuAtomicLoad(&this->uSleeping_)))
{
AuAtomicAdd(&this->uState_, uWaiters);
futex_wake(&this->uState_, uWaiters);
uWaitCount = uWaiters;
while (uWaiters &&
AuAtomicCompareExchange(&this->uSleeping_, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = AuAtomicLoad(&this->uSleeping_);
if (uWaiters <= uWaitCount)
{
uWaitCount = uWaiters;
}
}
}
}
AUKN_SYM IConditionVariable *ConditionVariableNew(const AuSPtr<IConditionMutex> &mutex)
{
return _new ConditionVariableImpl(mutex);
}
AUKN_SYM void ConditionVariableRelease(IConditionVariable *mutex)
{
AuSafeDelete<ConditionVariableImpl *>(mutex);
}
AUROXTL_INTERFACE_SOO_SRC(ConditionVariable, ConditionVariableImpl, (const AuSPtr<IConditionMutex> &, pMutex))
}
#endif