diff --git a/Include/Aurora/Threading/Waitables/FutexSemaphoreWaitable.hpp b/Include/Aurora/Threading/Waitables/FutexSemaphoreWaitable.hpp new file mode 100644 index 00000000..646a91da --- /dev/null +++ b/Include/Aurora/Threading/Waitables/FutexSemaphoreWaitable.hpp @@ -0,0 +1,157 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FutexSemaphoreWaitable.hpp + Date: 2023-08-19 + Author: Reece +***/ +#pragma once + +#include "../WakeOnAddress.hpp" +#include "../SpinTime.hpp" + +namespace Aurora::Threading::Waitables +{ + struct FutexSemaphoreWaitable final : IWaitable + { + inline constexpr FutexSemaphoreWaitable() + {} + + AU_NO_COPY_NO_MOVE(FutexSemaphoreWaitable); + + inline bool TryLock() override + { + { + auto uState = this->uAtomicState; + if (uState != 0 && AuAtomicCompareExchange(&this->uAtomicState, uState - 1, uState) == uState) + { + return true; + } + } + + #if defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_X64) + for (AU_ITERATE_N(i, AuUInt(1u) << AuUInt(Threading::GetSpinCountTimeout()))) + { + _mm_pause(); + + auto uState = this->uAtomicState; + if (uState != 0 && AuAtomicCompareExchange(&this->uAtomicState, uState - 1, uState) == uState) + { + return true; + } + } + #else + static const AuUInt32 kRef { 0 }; + if (TryWaitOnAddress(&this->uAtomicState, &kRef, sizeof(kRef))) + { + auto uState = this->uAtomicState; + if (uState != 0 && AuAtomicCompareExchange(&this->uAtomicState, uState - 1, uState) == uState) + { + return true; + } + } + #endif + + return false; + } + + inline bool HasOSHandle(AuMach &mach) override + { + return false; + } + + inline bool HasLockImplementation() override + { + return true; + } + + inline void Unlock() override + { + + } + + inline void NotifyOne() + { + NotifyN(1); + } + + inline void NotifyN(AuUInt8 uThreads) + { + AuAtomicAdd(&this->uAtomicState, AuUInt32(uThreads)); + + if (auto uSleeping = this->uAtomicSleeping) + { + WakeNOnAddress(&this->uAtomicState, uSleeping); + } + } + + inline void Lock() override + { + static const AuUInt32 kRef { 0 }; + + while (!TryLock()) + { + AuAtomicAdd(&this->uAtomicSleeping, 1u); + WaitOnAddress(&this->uAtomicState, &kRef, sizeof(kRef), 0); + AuAtomicSub(&this->uAtomicSleeping, 1u); + } + } + + inline bool LockMS(AuUInt64 qwTimeout) override + { + return LockNS(AuMSToNS(qwTimeout)); + } + + inline bool LockAbsMS(AuUInt64 qwTimeout) override + { + return LockAbsNS(AuMSToNS(qwTimeout)); + } + + inline bool LockNS(AuUInt64 qwTimeout) override + { + static const AuUInt32 kRef { 0 }; + + auto qwEndTime = Time::SteadyClockNS() + qwTimeout; + + while (!TryLock()) + { + bool bStatus {}; + + AuAtomicAdd(&this->uAtomicSleeping, 1u); + bStatus = WaitOnAddressSteady(&this->uAtomicState, &kRef, sizeof(kRef), qwEndTime); + AuAtomicSub(&this->uAtomicSleeping, 1u); + + if (!bStatus) + { + return TryLock(); + } + } + + return true; + } + + inline bool LockAbsNS(AuUInt64 qwTimeoutAbs) override + { + static const AuUInt32 kRef { 0 }; + + while (!TryLock()) + { + bool bStatus {}; + + AuAtomicAdd(&this->uAtomicSleeping, 1u); + bStatus = WaitOnAddressSteady(&this->uAtomicState, &kRef, sizeof(kRef), qwTimeoutAbs); + AuAtomicSub(&this->uAtomicSleeping, 1u); + + if (!bStatus) + { + return TryLock(); + } + } + + return true; + } + + AuUInt32 uAtomicState {}; + AuUInt32 uAtomicSleeping {}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Waitables/Waitables.hpp b/Include/Aurora/Threading/Waitables/Waitables.hpp index 848f6b90..80efe2cd 100644 --- a/Include/Aurora/Threading/Waitables/Waitables.hpp +++ b/Include/Aurora/Threading/Waitables/Waitables.hpp @@ -9,4 +9,5 @@ #include "CBWaitable.hpp" #include "BooleanWaitable.hpp" -#include "FutexWaitable.hpp" \ No newline at end of file +#include "FutexWaitable.hpp" +#include "FutexSemaphoreWaitable.hpp" \ No newline at end of file