/*** Copyright (C) 2022-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuSemaphore.Linux.cpp Date: 2022-12-28 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "SMTYield.hpp" #if !defined(_AURUNTIME_GENERIC_SEMAPHORE) #include namespace Aurora::Threading::Primitives { SemaphoreImpl::SemaphoreImpl(AuUInt16 uIntialValue) : dwState_(uIntialValue) { } SemaphoreImpl::~SemaphoreImpl() { } bool SemaphoreImpl::HasOSHandle(AuMach &mach) { return false; } bool SemaphoreImpl::HasLockImplementation() { return true; } bool SemaphoreImpl::TryLockNoSpin() { auto old = this->dwState_; return (old != 0 && AuAtomicCompareExchange(&this->dwState_, old - 1, old) == old); } bool SemaphoreImpl::TryLock() { if (ThrdCfg::gPreferLinuxSemaphoreSpinTryLock) { return this->TryLockHeavy(); } else { return this->TryLockNoSpin(); } } bool SemaphoreImpl::TryLockHeavy() { return DoTryIfAlderLake([=]() { return this->TryLockNoSpin(); }, &this->dwState_); } bool SemaphoreImpl::LockMS(AuUInt64 uTimeout) { return this->LockNS(AuMSToNS(uTimeout)); } bool SemaphoreImpl::LockNS(AuUInt64 uTimeout) { AuUInt64 uStart {}; AuUInt64 uEnd {}; if (this->TryLockNoSpin()) { return true; } errno = 0; 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->dwState_, 0, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->dwState_, 0)) == 0) { bStatus = true; continue; } if (ret == -EAGAIN) { bStatus = true; continue; } } while (ret == -EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret) } } AuAtomicSub(&this->dwSleeping_, 1u); return true; } bool SemaphoreImpl::LockAbsMS(AuUInt64 timeout) { return this->LockAbsNS(AuMSToNS(timeout)); } bool SemaphoreImpl::LockAbsNS(AuUInt64 uTimeout) { AuUInt64 uEnd {}; if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin) { if (this->TryLockNoSpin()) { return true; } } else { if (this->TryLockHeavy()) { return true; } } struct timespec tspec; if (uTimeout != 0) { uEnd = uTimeout; Time::monoabsns2ts(&tspec, uEnd); } 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->dwState_, 0, &tspec); } while (ret == -EINTR); } else { int ret {}; bool bStatus {}; do { if ((ret = futex_wait(&this->dwState_, 0)) == 0) { bStatus = true; continue; } if (ret == -EAGAIN) { bStatus = true; continue; } } while (ret == -EINTR); RUNTIME_ASSERT_SHUTDOWN_SAFE(bStatus, "semaphore wait failed: {}", ret) } } AuAtomicSub(&this->dwSleeping_, 1u); return true; } void SemaphoreImpl::Lock() { auto status = LockNS(0); SysAssert(status, "Couldn't lock semaphore"); } void SemaphoreImpl::Unlock(AuUInt16 count) { AuAtomicAdd(&this->dwState_, count); if (AuAtomicLoad(&this->dwSleeping_)) { futex_wake(&this->dwState_, count); } } void SemaphoreImpl::Unlock() { Unlock(1); } AUKN_SYM ISemaphore *SemaphoreNew(AuUInt16 uInitialCount) { return _new SemaphoreImpl(uInitialCount); } AUKN_SYM void SemaphoreRelease(ISemaphore *pSemaphore) { AuSafeDelete(pSemaphore); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Semaphore, SemaphoreImpl) } #endif