[*] Improve AuInitOnce API with an 8 byte variant and a locker API
[+] AuInitOnceSmall
This commit is contained in:
parent
d2bf01ffa3
commit
ff0e32ce02
@ -144,6 +144,7 @@ using AuFutexSemaphore = AuThreading::Waitables::FutexSemaphoreWaitable;
|
||||
using AuFutexCond = AuThreading::Waitables::FutexCondWaitable;
|
||||
|
||||
using AuInitOnce = AuThreading::InitOnce;
|
||||
using AuInitOnceSmall = __audetail::InitOnceABI;
|
||||
|
||||
template<typename T, bool bIsStatic = false>
|
||||
using AuTLSVariable = AuThreads::TLSVariable<T, bIsStatic>;
|
||||
|
@ -7,41 +7,70 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Threading
|
||||
{
|
||||
struct InitOnceLocker;
|
||||
}
|
||||
|
||||
namespace __audetail
|
||||
{
|
||||
struct InitOnceABI
|
||||
{
|
||||
inline bool IsUninitialized();
|
||||
|
||||
inline bool IsReady();
|
||||
|
||||
inline bool TrySet();
|
||||
|
||||
inline void Wait();
|
||||
|
||||
protected:
|
||||
|
||||
inline bool _TryAcquire();
|
||||
|
||||
inline void _Finish();
|
||||
|
||||
inline void _Wakeup();
|
||||
|
||||
inline bool _LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/);
|
||||
|
||||
protected:
|
||||
|
||||
AuAUInt32 uToken_ {};
|
||||
AuAUInt32 uSleepers_ {};
|
||||
|
||||
friend struct Aurora::Threading::InitOnceLocker;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Aurora::Threading
|
||||
{
|
||||
struct InitOnce final :
|
||||
private IWaitable
|
||||
private IWaitable,
|
||||
private __audetail::InitOnceABI
|
||||
{
|
||||
inline bool IsUninitialized()
|
||||
{
|
||||
return (AuAtomicLoad(&this->uToken_) & 1) == 0;
|
||||
return InitOnceABI::IsUninitialized();
|
||||
}
|
||||
|
||||
inline bool IsReady()
|
||||
{
|
||||
return (AuAtomicLoad(&this->uToken_) & 2) != 0;
|
||||
return InitOnceABI::IsReady();
|
||||
}
|
||||
|
||||
inline bool TrySet()
|
||||
{
|
||||
if (AuAtomicTestAndSet(&this->uToken_, 0) == 0)
|
||||
{
|
||||
this->Finish();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return InitOnceABI::TrySet();
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
bool TryInit(const Callable &callback)
|
||||
{
|
||||
if (AuAtomicTestAndSet(&this->uToken_, 0) == 0)
|
||||
if (this->_TryAcquire())
|
||||
{
|
||||
callback();
|
||||
this->Finish();
|
||||
this->_Finish();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -63,12 +92,12 @@ namespace Aurora::Threading
|
||||
return;
|
||||
}
|
||||
|
||||
this->Lock();
|
||||
this->Wait();
|
||||
}
|
||||
|
||||
inline void Wait()
|
||||
{
|
||||
this->Lock();
|
||||
InitOnceABI::Wait();
|
||||
}
|
||||
|
||||
inline IWaitable *ToBarrier()
|
||||
@ -77,39 +106,23 @@ namespace Aurora::Threading
|
||||
}
|
||||
|
||||
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)
|
||||
inline bool HasOSHandle(AuMach &mach) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool HasLockImplementation()
|
||||
inline bool HasLockImplementation() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void Lock()
|
||||
inline void Lock() override
|
||||
{
|
||||
this->LockAbsNS(0);
|
||||
}
|
||||
|
||||
inline bool LockNS(AuUInt64 qwTimeoutInNs)
|
||||
inline bool LockNS(AuUInt64 qwTimeoutInNs) override
|
||||
{
|
||||
if (this->IsReady())
|
||||
{
|
||||
@ -119,39 +132,133 @@ namespace Aurora::Threading
|
||||
return IWaitable::LockNS(qwTimeoutInNs);
|
||||
}
|
||||
|
||||
inline bool LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/)
|
||||
inline bool LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/) override
|
||||
{
|
||||
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;
|
||||
return this->_LockAbsNS(qwAbsTimeoutInNs);
|
||||
}
|
||||
|
||||
inline bool TryLock()
|
||||
inline bool TryLock() override
|
||||
{
|
||||
return this->IsReady();
|
||||
}
|
||||
|
||||
inline void Unlock()
|
||||
inline void Unlock() override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
AuAUInt32 uToken_ {};
|
||||
AuAUInt32 uSleepers_ {};
|
||||
friend struct InitOnceLocker;
|
||||
};
|
||||
|
||||
struct InitOnceLocker
|
||||
{
|
||||
cstatic bool TryLock(InitOnce *pInitOnce)
|
||||
{
|
||||
return pInitOnce->_TryAcquire();
|
||||
}
|
||||
|
||||
cstatic void Finish(InitOnce *pInitOnce)
|
||||
{
|
||||
pInitOnce->_Finish();
|
||||
}
|
||||
|
||||
cstatic bool TryLock(__audetail::InitOnceABI *pInitOnce)
|
||||
{
|
||||
return pInitOnce->_TryAcquire();
|
||||
}
|
||||
|
||||
cstatic void Finish(__audetail::InitOnceABI *pInitOnce)
|
||||
{
|
||||
pInitOnce->_Finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example usage:
|
||||
*
|
||||
* static AuInitOnceSmall gSmall; // or static AuInitOnce gInitOnce;
|
||||
* static_assert(sizeof(gSmall) == 8);
|
||||
*
|
||||
* if (AuThreading::InitOnceLocker::TryLock(&gSmall))
|
||||
* {
|
||||
* // My non-AuInitOnce::TryInit/Call() callback logic here
|
||||
* AuThreading::InitOnceLocker::Finish(&gSmall);
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* gSmall.Wait();
|
||||
* }
|
||||
*/
|
||||
};
|
||||
}
|
||||
|
||||
namespace __audetail
|
||||
{
|
||||
bool InitOnceABI::IsUninitialized()
|
||||
{
|
||||
return (AuAtomicLoad(&this->uToken_) & 1) == 0;
|
||||
}
|
||||
|
||||
bool InitOnceABI::IsReady()
|
||||
{
|
||||
return (AuAtomicLoad(&this->uToken_) & 2) != 0;
|
||||
}
|
||||
|
||||
bool InitOnceABI::TrySet()
|
||||
{
|
||||
if (this->_TryAcquire())
|
||||
{
|
||||
this->_Finish();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void InitOnceABI::Wait()
|
||||
{
|
||||
this->_LockAbsNS(0);
|
||||
}
|
||||
|
||||
bool InitOnceABI::_TryAcquire()
|
||||
{
|
||||
return AuAtomicTestAndSet(&this->uToken_, 0) == 0;
|
||||
}
|
||||
|
||||
void InitOnceABI::_Finish()
|
||||
{
|
||||
AuAtomicSet(&this->uToken_, 1);
|
||||
this->_Wakeup();
|
||||
}
|
||||
|
||||
void InitOnceABI::_Wakeup()
|
||||
{
|
||||
if (auto uSleepers = AuAtomicLoad(&this->uSleepers_))
|
||||
{
|
||||
Aurora::Threading::WakeNOnAddress((const void *)&this->uToken_, uSleepers);
|
||||
}
|
||||
}
|
||||
|
||||
bool InitOnceABI::_LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/)
|
||||
{
|
||||
auto uCurrent = AuAtomicLoad(&this->uToken_);
|
||||
|
||||
while ((uCurrent & 2) == 0)
|
||||
{
|
||||
AuAtomicAdd(&this->uSleepers_, 1u);
|
||||
bool bRet = Aurora::Threading::WaitOnAddressSteady((const void *)&this->uToken_, &uCurrent, sizeof(uCurrent), qwAbsTimeoutInNs);
|
||||
AuAtomicSub(&this->uSleepers_, 1u);
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
return this->IsReady();
|
||||
}
|
||||
|
||||
uCurrent = AuAtomicLoad(&this->uToken_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user