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

233 lines
5.2 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuConditionMutex.Linux.cpp
Date: 2023-8-11
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuConditionMutex.Generic.hpp"
#include "SMTYield.hpp"
#if !defined(_AURUNTIME_GENERICCV)
#include <Source/Time/Time.hpp>
namespace Aurora::Threading::Primitives
{
LinuxConditionMutex::LinuxConditionMutex()
{
}
LinuxConditionMutex::~LinuxConditionMutex()
{
}
bool LinuxConditionMutex::LockMS(AuUInt64 uTimeout)
{
return LockNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool LinuxConditionMutex::LockNS(AuUInt64 uTimeout)
{
if (TryLockNoSpin())
{
return true;
}
return LockNS(AuTime::SteadyClockNS() + uTimeout);
}
bool LinuxConditionMutex::LockAbsMS(AuUInt64 uTimeout)
{
return LockAbsNS(AuMSToNS<AuUInt64>(uTimeout));
}
bool LinuxConditionMutex::LockAbsNS(AuUInt64 uTimeout)
{
struct timespec tspec;
if (this->TryLockNoSpin())
{
return true;
}
if (uTimeout != 0)
{
Time::monoabsns2ts(&tspec, uTimeout);
}
if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{
if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
}
AuAtomicAdd(&this->uSleeping_, 1u);
auto state = this->uState_;
while (!(state == 0 &&
AuAtomicCompareExchange<AuUInt32>(&this->uState_, 1, state) == state))
{
if (uTimeout != 0)
{
if (Time::SteadyClockNS() >= uTimeout)
{
AuAtomicSub(&this->uSleeping_, 1u);
return false;
}
int ret {};
do
{
ret = futex_wait(&this->uState_, state, &tspec);
}
while (ret == -EINTR);
}
else
{
int ret {};
bool bStatus {};
do
{
if ((ret = futex_wait(&this->uState_, state)) == 0)
{
bStatus = true;
break;
}
if (ret == -EAGAIN)
{
bStatus = true;
break;
}
}
while (ret == -EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret)
}
state = this->uState_;
}
AuAtomicSub(&this->uSleeping_, 1u);
return true;
}
bool LinuxConditionMutex::TryLock()
{
if (gRuntimeConfig.threadingConfig.bPreferLinuxCondMutexSpinTryLock)
{
return TryLockHeavy();
}
else
{
return TryLockNoSpin();
}
}
bool LinuxConditionMutex::TryLockNoSpin()
{
return AuAtomicTestAndSet(&this->uState_, 0) == 0;
}
bool LinuxConditionMutex::TryLockHeavy()
{
return DoTryIf([=]()
{
return TryLockNoSpin();
});
}
void LinuxConditionMutex::Lock()
{
if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{
if (this->TryLockNoSpin())
{
return;
}
}
else
{
if (this->TryLockHeavy())
{
return;
}
}
AuAtomicAdd(&this->uSleeping_, 1u);
auto state = this->uState_;
while (!(state == 0 &&
AuAtomicCompareExchange<AuUInt32>(&this->uState_, 1, state) == state))
{
int ret {};
bool bStatus {};
do
{
if ((ret = futex_wait(&this->uState_, state)) == 0)
{
bStatus = true;
break;
}
if (ret == -EAGAIN)
{
bStatus = true;
break;
}
}
while (ret == -EINTR);
RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret)
state = this->uState_;
}
AuAtomicSub(&this->uSleeping_, 1u);
}
void LinuxConditionMutex::Unlock()
{
AuAtomicClearU8Lock(&this->uState_);
if (AuAtomicLoad(&this->uSleeping_))
{
futex_wake(&this->uState_, 1);
}
}
AuUInt LinuxConditionMutex::GetOSHandle()
{
return AuMach(&this->uState_);
}
AUKN_SYM IConditionMutex *ConditionMutexNew()
{
return _new ConditionMutexImpl();
}
AUKN_SYM void ConditionMutexRelease(IConditionMutex *mutex)
{
AuSafeDelete<ConditionMutexImpl *>(mutex);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, ConditionMutexImpl)
}
#endif