[*] 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 AuFutexCond = AuThreading::Waitables::FutexCondWaitable;
|
||||||
|
|
||||||
using AuInitOnce = AuThreading::InitOnce;
|
using AuInitOnce = AuThreading::InitOnce;
|
||||||
|
using AuInitOnceSmall = __audetail::InitOnceABI;
|
||||||
|
|
||||||
template<typename T, bool bIsStatic = false>
|
template<typename T, bool bIsStatic = false>
|
||||||
using AuTLSVariable = AuThreads::TLSVariable<T, bIsStatic>;
|
using AuTLSVariable = AuThreads::TLSVariable<T, bIsStatic>;
|
||||||
|
@ -7,41 +7,70 @@
|
|||||||
***/
|
***/
|
||||||
#pragma once
|
#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
|
namespace Aurora::Threading
|
||||||
{
|
{
|
||||||
struct InitOnce final :
|
struct InitOnce final :
|
||||||
private IWaitable
|
private IWaitable,
|
||||||
|
private __audetail::InitOnceABI
|
||||||
{
|
{
|
||||||
inline bool IsUninitialized()
|
inline bool IsUninitialized()
|
||||||
{
|
{
|
||||||
return (AuAtomicLoad(&this->uToken_) & 1) == 0;
|
return InitOnceABI::IsUninitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool IsReady()
|
inline bool IsReady()
|
||||||
{
|
{
|
||||||
return (AuAtomicLoad(&this->uToken_) & 2) != 0;
|
return InitOnceABI::IsReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool TrySet()
|
inline bool TrySet()
|
||||||
{
|
{
|
||||||
if (AuAtomicTestAndSet(&this->uToken_, 0) == 0)
|
return InitOnceABI::TrySet();
|
||||||
{
|
|
||||||
this->Finish();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
bool TryInit(const Callable &callback)
|
bool TryInit(const Callable &callback)
|
||||||
{
|
{
|
||||||
if (AuAtomicTestAndSet(&this->uToken_, 0) == 0)
|
if (this->_TryAcquire())
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
this->Finish();
|
this->_Finish();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -63,12 +92,12 @@ namespace Aurora::Threading
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->Lock();
|
this->Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Wait()
|
inline void Wait()
|
||||||
{
|
{
|
||||||
this->Lock();
|
InitOnceABI::Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IWaitable *ToBarrier()
|
inline IWaitable *ToBarrier()
|
||||||
@ -77,39 +106,23 @@ namespace Aurora::Threading
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool HasLockImplementation()
|
inline bool HasLockImplementation() override
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Lock()
|
inline void Lock() override
|
||||||
{
|
{
|
||||||
this->LockAbsNS(0);
|
this->LockAbsNS(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool LockNS(AuUInt64 qwTimeoutInNs)
|
inline bool LockNS(AuUInt64 qwTimeoutInNs) override
|
||||||
{
|
{
|
||||||
if (this->IsReady())
|
if (this->IsReady())
|
||||||
{
|
{
|
||||||
@ -119,39 +132,133 @@ namespace Aurora::Threading
|
|||||||
return IWaitable::LockNS(qwTimeoutInNs);
|
return IWaitable::LockNS(qwTimeoutInNs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/)
|
inline bool LockAbsNS(AuUInt64 qwAbsTimeoutInNs /* = 0, infinity*/) override
|
||||||
{
|
{
|
||||||
auto uCurrent = AuAtomicLoad(&this->uToken_);
|
return this->_LockAbsNS(qwAbsTimeoutInNs);
|
||||||
|
|
||||||
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()
|
inline bool TryLock() override
|
||||||
{
|
{
|
||||||
return this->IsReady();
|
return this->IsReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Unlock()
|
inline void Unlock() override
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AuAUInt32 uToken_ {};
|
friend struct InitOnceLocker;
|
||||||
AuAUInt32 uSleepers_ {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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