diff --git a/Include/Aurora/Threading/Waitables/FutexWaitable.hpp b/Include/Aurora/Threading/Waitables/FutexWaitable.hpp new file mode 100644 index 00000000..e7412bd8 --- /dev/null +++ b/Include/Aurora/Threading/Waitables/FutexWaitable.hpp @@ -0,0 +1,150 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FutexWaitable.hpp + Date: 2023-08-19 + Author: Reece +***/ +#pragma once + +#include "../WakeOnAddress.hpp" +#include "../SpinTime.hpp" + +namespace Aurora::Threading::Waitables +{ + struct FutexWaitable final : IWaitable + { + inline constexpr FutexWaitable() + {} + + AU_NO_COPY_NO_MOVE(FutexWaitable); + + inline bool TryLock() override + { + if (AuAtomicTestAndSet(&this->uAtomicState, 0u)) + { + return true; + } + + #if defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_X64) + for (AU_ITERATE_N(i, AuUInt(1u) << AuUInt(Threading::GetSpinCountTimeout()))) + { + _mm_pause(); + + if (AuAtomicTestAndSet(&this->uAtomicState, 0u)) + { + return true; + } + } + #else + static const AuUInt32 kRef { 1 }; + if (TryWaitOnAddress(&this->uAtomicState, &kRef, sizeof(kRef))) + { + if (AuAtomicTestAndSet(&this->uAtomicState, 0u)) + { + return true; + } + } + #endif + + return false; + } + + inline bool HasOSHandle(AuMach &mach) override + { + return false; + } + + inline bool HasLockImplementation() override + { + return true; + } + + inline void Unlock() override + { + this->uAtomicState = 0; + + if (auto uSleeping = this->uAtomicSleeping) + { + WakeOnAddress(&this->uAtomicState); + } + } + + inline void Lock() override + { + static const AuUInt32 kRef { 1 }; + + 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 { 1 }; + if (AuAtomicTestAndSet(&this->uAtomicState, 0u)) + { + return true; + } + + 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 { 1 }; + if (AuAtomicTestAndSet(&this->uAtomicState, 0u)) + { + return true; + } + + 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 e9f7674a..848f6b90 100644 --- a/Include/Aurora/Threading/Waitables/Waitables.hpp +++ b/Include/Aurora/Threading/Waitables/Waitables.hpp @@ -8,4 +8,5 @@ #pragma once #include "CBWaitable.hpp" -#include "BooleanWaitable.hpp" \ No newline at end of file +#include "BooleanWaitable.hpp" +#include "FutexWaitable.hpp" \ No newline at end of file