/*** Copyright (C) 2021-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuSemaphore.Generic.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "AuSemaphore.Generic.hpp" #include "../AuWakeInternal.hpp" #include "SMTYield.hpp" #if defined(_AURUNTIME_GENERIC_SEMAPHORE) namespace Aurora::Threading::Primitives { SemaphoreGeneric::SemaphoreGeneric(AuUInt16 uIntialValue) : uAtomicState(uIntialValue), uAtomicSleeping(0) { } SemaphoreGeneric::~SemaphoreGeneric() { } bool SemaphoreGeneric::TryLockNoSpin() { auto uState = this->uAtomicState; return (uState != 0 && AuAtomicCompareExchange(&this->uAtomicState, uState - 1, uState) == uState); } bool SemaphoreGeneric::TryLockHeavy() { return DoTryIfAlderLake([=]() { return this->TryLockNoSpin(); }, &this->uAtomicState); } bool SemaphoreGeneric::TryLock() { if (ThrdCfg::gPreferUnixPrimitivesNoSpin) { return this->TryLockNoSpin(); } else { return this->TryLockHeavy(); } } bool SemaphoreGeneric::HasOSHandle(AuMach &mach) { return false; } bool SemaphoreGeneric::HasLockImplementation() { return true; } void SemaphoreGeneric::Unlock() { this->Unlock(1); } void SemaphoreGeneric::Unlock(AuUInt16 uThreads) { AuAtomicAdd(&this->uAtomicState, AuUInt32(uThreads)); if (auto uSleeping = AuAtomicLoad(&this->uAtomicSleeping)) { InternalLTSWakeCount((const void *)&this->uAtomicState, AuMin(uSleeping, uThreads)); } } void SemaphoreGeneric::Lock() { static const AuUInt32 kRef { 0 }; if (this->TryLock()) { return; } while (!this->TryLockNoSpin()) { AuAtomicAdd(&this->uAtomicSleeping, 1u); InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), 0); AuAtomicSub(&this->uAtomicSleeping, 1u); } } bool SemaphoreGeneric::LockMS(AuUInt64 qwTimeout) { return LockNS(AuMSToNS(qwTimeout)); } bool SemaphoreGeneric::LockAbsMS(AuUInt64 qwTimeout) { return LockAbsNS(AuMSToNS(qwTimeout)); } bool SemaphoreGeneric::LockNS(AuUInt64 qwTimeout) { static const AuUInt32 kRef { 0 }; if (this->TryLockNoSpin()) { return true; } auto qwEndTime = qwTimeout ? Time::SteadyClockNS() + qwTimeout : 0; if (!ThrdCfg::gPreferUnixPrimitivesNoSpin) { if (this->TryLockHeavy()) { return true; } } while (!this->TryLockNoSpin()) { bool bStatus {}; AuAtomicAdd(&this->uAtomicSleeping, 1u); bStatus = InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwEndTime); AuAtomicSub(&this->uAtomicSleeping, 1u); if (!bStatus) { return TryLockNoSpin(); } } return true; } bool SemaphoreGeneric::LockAbsNS(AuUInt64 qwTimeoutAbs) { static const AuUInt32 kRef { 0 }; if (!ThrdCfg::gPreferUnixPrimitivesNoSpin) { if (this->TryLockHeavy()) { return true; } } while (!TryLockNoSpin()) { bool bStatus {}; AuAtomicAdd(&this->uAtomicSleeping, 1u); bStatus = InternalLTSWaitOnAddressHighRes((const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwTimeoutAbs); AuAtomicSub(&this->uAtomicSleeping, 1u); if (!bStatus) { return TryLockNoSpin(); } } return true; } AUKN_SYM ISemaphore *SemaphoreNew(AuUInt16 uIntialValue) { return _new SemaphoreGeneric(uIntialValue); } AUKN_SYM void SemaphoreRelease(ISemaphore *waitable) { AuSafeDelete(waitable); } AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, Semaphore, SemaphoreGeneric) } #endif