157 lines
3.2 KiB
C++
157 lines
3.2 KiB
C++
|
/***
|
||
|
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 <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_ {};
|
||
|
};
|
||
|
}
|