/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: InitOnce.hpp Date: 2023-09-17 Author: Reece ***/ #pragma once namespace Aurora::Threading { struct InitOnce final : private IWaitable { inline bool IsUninitialized() { return (AuAtomicLoad(&this->uToken_) & 1) == 0; } inline bool IsReady() { return (AuAtomicLoad(&this->uToken_) & 2) != 0; } inline bool TrySet() { if (AuAtomicTestAndSet(&this->uToken_, 0) == 0) { this->Finish(); return true; } else { return false; } } template bool TryInit(const Callable &callback) { if (AuAtomicTestAndSet(&this->uToken_, 0) == 0) { callback(); this->Finish(); return true; } else { return false; } } template void Call(const Callable &callback) { if (this->TryInit(callback)) { return; } if (this->IsReady()) { return; } this->Lock(); } inline void Wait() { this->Lock(); } inline IWaitable *ToBarrier() { return this; } protected: inline void Finish() { AuAtomicSet(&this->uToken_, 1); this->Wakeup(); } // barrier impl: inline void Wakeup() { if (auto uSleepers = AuAtomicLoad(&this->uSleepers_)) { WakeNOnAddress((const void *)&this->uToken_, uSleepers); } } inline bool HasOSHandle(AuMach &mach) { return false; } inline bool HasLockImplementation() { return true; } inline void Lock() { this->LockAbsNS(0); } inline bool LockNS(AuUInt64 qwTimeoutInNs) { if (this->IsReady()) { return true; } return IWaitable::LockNS(qwTimeoutInNs); } inline bool LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/) { auto uCurrent = AuAtomicLoad(&this->uToken_); while ((uCurrent & 2) == 0) { AuAtomicAdd(&this->uSleepers_, 1u); bool bRet = WaitOnAddressSteady((const void *)&this->uToken_, &uCurrent, sizeof(uCurrent), qwAbsTimeoutInNs); AuAtomicSub(&this->uSleepers_, 1u); if (!bRet) { return this->IsReady(); } uCurrent = AuAtomicLoad(&this->uToken_); } return true; } inline bool TryLock() { return this->IsReady(); } inline void Unlock() { } private: AuAUInt32 uToken_ {}; AuAUInt32 uSleepers_ {}; }; }