AuroraRuntime/Include/Aurora/Threading/Waitables/FutexCondWaitable.hpp
2024-05-28 19:28:08 +01:00

221 lines
6.1 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FutexCondWaitable.hpp
Date: 2023-08-19
Author: Reece
***/
#pragma once
#include "../WakeOnAddress.hpp"
#include "../SpinTime.hpp"
#include "_.inl"
namespace Aurora::Threading::Waitables
{
struct FutexCondWaitable final
{
AURT_WAITABLE_NULL_CTORS_DEF(FutexCondWaitable)
template <class T>
inline bool WaitNS(T pWaitable, AuUInt64 uRelativeNanoseconds)
{
AuAtomicAdd(&this->uAtomicSleeping, 1u);
if (pWaitable)
{
pWaitable->Unlock();
}
auto bSuccess = this->TryLock2() ||
this->SleepOne(uRelativeNanoseconds ? Time::SteadyClockNS() + uRelativeNanoseconds : 0);
if (!bSuccess)
{
auto uWaiters = this->uAtomicSleeping;
auto uWaitCount = 1;
while (uWaiters &&
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uAtomicSleeping;
if (uWaiters == 0)
{
break;
}
}
}
if (pWaitable)
{
pWaitable->Lock();
}
return bSuccess;
}
template <class T>
inline bool WaitAbsNS(T pWaitable, AuUInt64 uSteadyClockNanoseconds)
{
AuAtomicAdd(&this->uAtomicSleeping, 1u);
if (pWaitable)
{
pWaitable->Unlock();
}
auto bSuccess = this->TryLock2() ||
this->SleepOne(uSteadyClockNanoseconds);
if (!bSuccess)
{
auto uWaiters = this->uAtomicSleeping;
auto uWaitCount = 1;
while (uWaiters &&
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uAtomicSleeping;
if (uWaiters == 0)
{
break;
}
}
}
if (pWaitable)
{
pWaitable->Lock();
}
return bSuccess;
}
inline auline void NotifyOne()
{
AuUInt32 uWaiters = AuAtomicLoad(&this->uAtomicSleeping);
if (uWaiters == 0)
{
return;
}
AuAtomicAdd(&this->uAtomicState, 1u);
WakeOnAddress((const void *)&this->uAtomicState);
while (AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - 1u, uWaiters) != uWaiters)
{
uWaiters = this->uAtomicSleeping;
if (uWaiters == 0)
{
return;
}
}
}
inline auline void Broadcast()
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
#if defined(AURORA_RUNTIME_FUTEX_AGGRESSIVE_COND_WAKE)
while ((uWaiters = AuAtomicLoad(&this->uAtomicSleeping)))
#else
if ((uWaiters = AuAtomicLoad(&this->uAtomicSleeping)))
#endif
{
AuAtomicAdd(&this->uAtomicState, uWaiters);
WakeNOnAddress((const void *)&this->uAtomicState, uWaiters);
uWaitCount = uWaiters;
while (uWaiters &&
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uAtomicSleeping;
if (uWaiters <= uWaitCount)
{
uWaitCount = uWaiters;
}
}
}
}
inline auline void NotifyN(AuUInt32 uThreads)
{
AuUInt32 uWaitCount {};
AuUInt32 uWaiters {};
uWaiters = AuAtomicLoad(&this->uAtomicSleeping);
if (uWaiters > 0)
{
auto uMin = AuMin(uWaiters, uThreads);
AuAtomicAdd(&this->uAtomicState, uMin);
WakeNOnAddress((const void *)&this->uAtomicState, uMin);
uWaitCount = uMin;
}
while (uWaiters &&
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
{
uWaiters = this->uAtomicSleeping;
if (uWaiters <= uWaitCount)
{
uWaitCount = uWaiters;
}
}
}
AuAUInt32 uAtomicState {};
AuAUInt32 uAtomicSleeping {};
private:
inline auline bool TryLock3()
{
auto old = AuAtomicLoad(&this->uAtomicState);
return ((old != 0 && AuAtomicCompareExchange(&this->uAtomicState, old - 1, old) == old));
}
inline auline bool TryLock2()
{
static const AuUInt32 kRef { 0 };
if (TryLock3())
{
return true;
}
return TryWaitOnAddressEx((const void *)&this->uAtomicState,
&kRef,
sizeof(kRef),
[&](const void *pTargetAddress,
const void *pCompareAddress,
AuUInt8 uWordSize)
{
return this->TryLock3();
});
}
inline auline bool SleepOne(AuUInt64 qwTimeout)
{
static const AuUInt32 kRef { 0 };
while (!TryLock2())
{
bool bStatus {};
bStatus = WaitOnAddressSteady((const void *)&this->uAtomicState, &kRef, sizeof(kRef), qwTimeout, true);
if (!bStatus)
{
return TryLock2();
}
}
return true;
}
};
}