213 lines
4.8 KiB
C++
213 lines
4.8 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 (TryLockNoSpin())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (uTimeout != 0)
|
|
{
|
|
Time::monoabsns2ts(&tspec, uTimeout);
|
|
}
|
|
|
|
if (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 || errno == 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 (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 || errno == 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 |