/*** Copyright (C) 2022-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuMutex.Linux.cpp Date: 2022-12-28 Author: Reece ***/ #include #include "AuMutex.Generic.hpp" #include "SMTYield.hpp" #if !defined(_AURUNTIME_GENERIC_MUTEX) #include namespace Aurora::Threading::Primitives { MutexImpl::MutexImpl() { this->state_ = 0; } MutexImpl::~MutexImpl() { } bool MutexImpl::HasOSHandle(AuMach &mach) { return false; } bool MutexImpl::HasLockImplementation() { return true; } bool MutexImpl::TryLock() { if (this->TryLockNoSpin()) { return true; } if (!ThrdCfg::gPreferLinuxMutexSpinTryLock) { return false; } return this->TryLockHeavy(); } bool MutexImpl::TryLockNoSpin() { return AuAtomicTestAndSet(&this->state_, 0) == 0; } bool MutexImpl::TryLockHeavy() { return DoTryIfAlderLake([=]() { return this->TryLockNoSpin(); }, &this->state_); } bool MutexImpl::LockMS(AuUInt64 uTimeout) { return this->LockNS(AuMSToNS(uTimeout)); } bool MutexImpl::LockNS(AuUInt64 uTimeout) { AuUInt64 uStart {}; AuUInt64 uEnd {}; if (this->TryLockNoSpin()) { return true; } struct timespec tspec; if (uTimeout != 0) { uStart = AuTime::SteadyClockNS(); uEnd = uStart + uTimeout; Time::monoabsns2ts(&tspec, uEnd); } if (!ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin) { if (this->TryLockHeavy()) { return true; } } AuAtomicAdd(&this->dwSleeping_, 1u); while (!this->TryLockNoSpin()) { if (uTimeout != 0) { if (Time::SteadyClockNS() >= uEnd) { AuAtomicSub(&this->dwSleeping_, 1u); return false; } int ret {}; do { ret = futex_wait(&this->state_, 1, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->state_, 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->dwSleeping_, 1u); return true; } bool MutexImpl::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->dwSleeping_, 1u); while (!this->TryLockNoSpin()) { if (uTimeout != 0) { if (Time::SteadyClockNS() >= uTimeout) { AuAtomicSub(&this->dwSleeping_, 1u); return false; } int ret {}; do { ret = futex_wait(&this->state_, 1, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->state_, 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->dwSleeping_, 1u); return true; } void MutexImpl::SlowLock() { auto status = LockMS(0); SysAssert(status, "Couldn't lock mutex"); } void MutexImpl::Unlock() { AuAtomicClearU8Lock(&this->state_); if (AuAtomicLoad(&this->dwSleeping_)) { futex_wake(&this->state_, 1); } } AUKN_SYM IHyperWaitable *MutexNew() { return _new MutexImpl(); } AUKN_SYM void MutexRelease(IHyperWaitable *pMutex) { AuSafeDelete(pMutex); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Mutex, MutexImpl) } #endif