diff --git a/Include/Aurora/Runtime.hpp b/Include/Aurora/Runtime.hpp index e6db916e..51b7be15 100644 --- a/Include/Aurora/Runtime.hpp +++ b/Include/Aurora/Runtime.hpp @@ -130,8 +130,8 @@ using AuMemoryViewStreamWrite = AuMemory::MemoryViewStreamWrite; using AuMutex = AuThreadPrimitives::Mutex; using AuSemaphore = AuThreadPrimitives::Semaphore; -using AuRWRenterableLock = AuThreadPrimitives::RWRenterableLock; using AuRenterableMutex = AuThreadPrimitives::CriticalSection; +using AuRWRenterableLock = AuThreadPrimitives::RWRenterableLock; using AuRWLock = AuThreadPrimitives::RWLock; using AuCond = AuThreadPrimitives::ConditionVariable; using AuCondMutex = AuThreadPrimitives::ConditionMutex; @@ -143,6 +143,8 @@ using AuFutexMutex = AuThreading::Waitables::FutexWaitable; using AuFutexSemaphore = AuThreading::Waitables::FutexSemaphoreWaitable; using AuFutexCond = AuThreading::Waitables::FutexCondWaitable; +using AuInitOnce = AuThreading::InitOnce; + template using AuTLSVariable = AuThreads::TLSVariable; diff --git a/Include/Aurora/Threading/InitOnce.hpp b/Include/Aurora/Threading/InitOnce.hpp new file mode 100644 index 00000000..a7ce7112 --- /dev/null +++ b/Include/Aurora/Threading/InitOnce.hpp @@ -0,0 +1,157 @@ +/*** + 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 : + 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_ {}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threading.hpp b/Include/Aurora/Threading/Threading.hpp index 8e72483b..219e7ebf 100644 --- a/Include/Aurora/Threading/Threading.hpp +++ b/Include/Aurora/Threading/Threading.hpp @@ -26,4 +26,6 @@ namespace Aurora::Threading #include "SpinTime.hpp" -#include "Threads/Threads.hpp" \ No newline at end of file +#include "Threads/Threads.hpp" + +#include "InitOnce.hpp" \ No newline at end of file