AuroraRuntime/Include/Aurora/Threading/InitOnce.hpp

157 lines
3.2 KiB
C++
Raw Normal View History

2023-09-17 03:40:15 +00:00
/***
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 :
2023-09-17 03:40:15 +00:00
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 <typename Callable>
bool TryInit(const Callable &callback)
{
if (AuAtomicTestAndSet(&this->uToken_, 0) == 0)
{
callback();
this->Finish();
return true;
}
else
{
return false;
}
}
template <typename Callable>
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_ {};
};
}