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_ {};
|
|
};
|
|
} |