/*** Copyright (C) 2023-2024 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 LockAbsNS(uTimeout ? Time::SteadyClockNS() + uTimeout : 0); } bool LinuxConditionMutex::LockAbsMS(AuUInt64 uTimeout) { return LockAbsNS(AuMSToNS(uTimeout)); } bool LinuxConditionMutex::LockAbsNS(AuUInt64 uTimeout) { struct timespec tspec; if (this->TryLockNoSpin()) { return true; } if (uTimeout != 0) { Time::monoabsns2ts(&tspec, uTimeout); } if (!ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin) { if (this->TryLockHeavy()) { return true; } } AuAtomicAdd(&this->uSleeping_, 1u); while (!this->TryLockNoSpin()) { if (uTimeout != 0) { if (Time::SteadyClockNS() >= uTimeout) { AuAtomicSub(&this->uSleeping_, 1u); return false; } int ret {}; do { ret = futex_wait(&this->uState_, 1, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->uState_, 1)) == 0) { bStatus = true; break; } if (ret == -EAGAIN) { bStatus = true; break; } } while (ret == -EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret) } } AuAtomicSub(&this->uSleeping_, 1u); return true; } bool LinuxConditionMutex::TryLock() { if (ThrdCfg::gPreferLinuxCondMutexSpinTryLock) { return TryLockHeavy(); } else { return TryLockNoSpin(); } } bool LinuxConditionMutex::TryLockNoSpin() { return AuAtomicTestAndSet(&this->uState_, 0) == 0; } bool LinuxConditionMutex::TryLockHeavy() { return DoTryIfAlderLake([=]() { return TryLockNoSpin(); }, &this->uState_); } void LinuxConditionMutex::Lock() { if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin) { if (this->TryLockNoSpin()) { return; } } else { if (this->TryLockHeavy()) { return; } } AuAtomicAdd(&this->uSleeping_, 1u); while (!this->TryLockNoSpin()) { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->uState_, 1)) == 0) { bStatus = true; break; } if (ret == -EAGAIN) { bStatus = true; break; } } while (ret == -EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "Mutex wait failed: {}", ret) } 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