/*** Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved. File: FutexCountdown.hpp Date: 2024-06-30 Author: Reece ***/ #pragma once #include "../WakeOnAddress.hpp" #include "../SpinTime.hpp" #include "_.inl" namespace Aurora::Threading::Waitables { struct FutexCountdown final : IWaitable { inline constexpr FutexCountdown() {} inline constexpr FutexCountdown(AuUInt32 uUsers) : uAtomicState(uUsers) { } inline constexpr FutexCountdown(const FutexCountdown &that) : uAtomicState(that.uAtomicState) { } inline constexpr FutexCountdown(FutexCountdown &&that) : uAtomicState(that.uAtomicState) { } AURT_WAITABLE_COPY_OPERATORS(FutexCountdown) inline auline void AddUser() { AuAtomicAdd(&this->uAtomicState, 1u); } inline auline void AddUsers(AuUInt32 uCount) { AuAtomicAdd(&this->uAtomicState, uCount); } inline auline void DecrementUser() { if (!AuAtomicSub(&this->uAtomicState, 1u)) { if (AuAtomicLoad(&this->uAtomicSleeping)) { WakeAllOnAddress((const void *)&this->uAtomicState); } } } inline auline void DecrementUsers(AuUInt32 uCount) { if (!AuAtomicSub(&this->uAtomicState, uCount)) { if (AuAtomicLoad(&this->uAtomicSleeping)) { WakeAllOnAddress((const void *)&this->uAtomicState); } } } inline auline bool TryLockNoSpin() { return AuAtomicLoad(&this->uAtomicState) == 0; } inline bool TryLock() override { static const AuUInt32 kRef { 0 }; if (this->TryLockNoSpin()) { return true; } return TryWaitOnAddressSpecial(EWaitMethod::eEqual, (const void *)&this->uAtomicState, &kRef, sizeof(kRef)); } inline bool HasOSHandle(AuMach &mach) override { return false; } inline bool HasLockImplementation() override { return true; } inline void Unlock() override { } inline void Lock() override { static const AuUInt32 kRef { 0 }; if (this->TryLock()) { return; } while (!this->TryLockNoSpin()) { AuAtomicAdd(&this->uAtomicSleeping, 1u); WaitOnAddressSpecial(EWaitMethod::eEqual, (const void *)&this->uAtomicState, &kRef, sizeof(kRef), 0, true); 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 }; if (this->TryLockNoSpin()) { return true; } auto qwEndTime = qwTimeout ? Time::SteadyClockNS() + qwTimeout : 0; if (this->TryLock()) { return true; } while (!this->TryLockNoSpin()) { bool bStatus {}; AuAtomicAdd(&this->uAtomicSleeping, 1u); bStatus = WaitOnAddressSpecialSteady(EWaitMethod::eEqual, (const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwEndTime, true); AuAtomicSub(&this->uAtomicSleeping, 1u); if (!bStatus) { return this->TryLock(); } } return true; } inline bool LockAbsNS(AuUInt64 qwTimeoutAbs) override { static const AuUInt32 kRef { 0 }; if (this->TryLockNoSpin()) { return true; } while (!this->TryLockNoSpin()) { bool bStatus {}; AuAtomicAdd(&this->uAtomicSleeping, 1u); bStatus = WaitOnAddressSpecialSteady(EWaitMethod::eEqual, (const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwTimeoutAbs, true); AuAtomicSub(&this->uAtomicSleeping, 1u); if (!bStatus) { return this->TryLock(); } } return true; } AuAUInt32 uAtomicState {}; AuAUInt32 uAtomicSleeping {}; }; }