/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuConditionMutex.Linux.cpp Date: 2023-8-11 Author: Reece ***/ #include #include "AuConditionMutex.Generic.hpp" #include "SMTYield.hpp" #if !defined(_AURUNTIME_GENERICCV) #include namespace Aurora::Threading::Primitives { LinuxConditionMutex::LinuxConditionMutex() { } LinuxConditionMutex::~LinuxConditionMutex() { } bool LinuxConditionMutex::LockMS(AuUInt64 uTimeout) { return LockNS(AuMSToNS(uTimeout)); } bool LinuxConditionMutex::LockNS(AuUInt64 uTimeout) { if (TryLockNoSpin()) { return true; } return LockNS(AuTime::SteadyClockNS() + uTimeout); } bool LinuxConditionMutex::LockAbsMS(AuUInt64 uTimeout) { return LockAbsNS(AuMSToNS(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(&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(&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(mutex); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, ConditionMutex, ConditionMutexImpl) } #endif