2023-08-19 21:25:31 +00:00
|
|
|
/***
|
|
|
|
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"
|
|
|
|
|
|
|
|
namespace Aurora::Threading::Waitables
|
|
|
|
{
|
|
|
|
struct FutexCondWaitable final
|
|
|
|
{
|
|
|
|
inline constexpr FutexCondWaitable()
|
|
|
|
{}
|
|
|
|
|
|
|
|
AU_NO_COPY_NO_MOVE(FutexCondWaitable);
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
inline bool WaitNS(T pWaitable, AuUInt64 uRelativeNanoseconds)
|
|
|
|
{
|
|
|
|
AuAtomicAdd(&this->uAtomicSleeping, 1u);
|
|
|
|
|
|
|
|
if (pWaitable)
|
|
|
|
{
|
|
|
|
pWaitable->Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto bSuccess = this->TryLock2() ||
|
2023-08-19 21:29:09 +00:00
|
|
|
this->SleepOne(uRelativeNanoseconds ? Time::SteadyClockNS() + uRelativeNanoseconds : 0);
|
2023-08-19 21:25:31 +00:00
|
|
|
|
|
|
|
if (!bSuccess)
|
|
|
|
{
|
|
|
|
auto uWaiters = this->uAtomicSleeping;
|
|
|
|
auto uWaitCount = 1;
|
|
|
|
|
2023-08-20 23:25:29 +00:00
|
|
|
while (uWaiters &&
|
|
|
|
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2023-08-20 23:25:29 +00:00
|
|
|
while (uWaiters &&
|
|
|
|
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
|
|
|
|
if (uWaiters == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pWaitable)
|
|
|
|
{
|
|
|
|
pWaitable->Lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
return bSuccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
auline void NotifyOne()
|
|
|
|
{
|
|
|
|
AuUInt32 uWaitCount {};
|
|
|
|
AuUInt32 uWaiters {};
|
|
|
|
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
if (uWaiters > 0)
|
|
|
|
{
|
|
|
|
AuAtomicAdd(&this->uAtomicState, 1u);
|
|
|
|
WakeOnAddress((const void *)&this->uAtomicState);
|
|
|
|
uWaitCount = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
|
|
|
|
{
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
|
|
|
|
if (uWaiters == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auline void Broadcast()
|
|
|
|
{
|
|
|
|
AuUInt32 uWaitCount {};
|
|
|
|
AuUInt32 uWaiters {};
|
|
|
|
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
if (uWaiters > 0)
|
|
|
|
{
|
|
|
|
AuAtomicAdd(&this->uAtomicState, uWaiters);
|
|
|
|
WakeNOnAddress((const void *)&this->uAtomicState, uWaiters);
|
2023-08-22 08:44:54 +00:00
|
|
|
uWaitCount = uWaiters;
|
2023-08-19 21:25:31 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 08:44:54 +00:00
|
|
|
while (uWaiters &&
|
|
|
|
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
|
|
|
|
if (uWaiters == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auline void NotifyN(AuUInt32 uThreads)
|
|
|
|
{
|
|
|
|
AuUInt32 uWaitCount {};
|
|
|
|
AuUInt32 uWaiters {};
|
|
|
|
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
if (uWaiters > 0)
|
|
|
|
{
|
2023-08-22 08:44:54 +00:00
|
|
|
auto uMin = AuMin(uWaiters, uThreads);
|
|
|
|
AuAtomicAdd(&this->uAtomicState, uMin);
|
|
|
|
WakeNOnAddress((const void *)&this->uAtomicState, uMin);
|
|
|
|
uWaitCount = uMin;
|
2023-08-19 21:25:31 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 08:44:54 +00:00
|
|
|
while (uWaiters &&
|
|
|
|
AuAtomicCompareExchange(&this->uAtomicSleeping, uWaiters - uWaitCount, uWaiters) != uWaiters)
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
uWaiters = this->uAtomicSleeping;
|
|
|
|
|
2023-08-22 08:44:54 +00:00
|
|
|
if (uWaiters <= uWaitCount)
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
2023-08-22 08:44:54 +00:00
|
|
|
uWaitCount = uWaiters;
|
2023-08-19 21:25:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
volatile AuUInt32 uAtomicState {};
|
|
|
|
volatile AuUInt32 uAtomicSleeping {};
|
|
|
|
private:
|
|
|
|
|
2023-08-20 16:11:50 +00:00
|
|
|
auline bool TryLock3()
|
|
|
|
{
|
|
|
|
auto old = this->uAtomicState;
|
|
|
|
return ((old != 0 && AuAtomicCompareExchange(&this->uAtomicState, old - 1, old) == old));
|
|
|
|
}
|
|
|
|
|
2023-08-19 21:25:31 +00:00
|
|
|
auline bool TryLock2()
|
|
|
|
{
|
2023-08-20 16:11:50 +00:00
|
|
|
if (TryLock3())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-22 12:58:17 +00:00
|
|
|
#if (defined(AURORA_ARCH_X86) || defined(AURORA_ARCH_X64)) && !defined(AURORA_RUNTIME_FORCE_ADAPTIVE_FUTEX)
|
2023-08-20 16:11:50 +00:00
|
|
|
AuUInt uCount(GetTotalSpinCountTimeout());
|
|
|
|
for (AU_ITERATE_N(i, uCount))
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
_mm_pause();
|
|
|
|
|
2023-08-20 16:11:50 +00:00
|
|
|
if (TryLock3())
|
2023-08-19 21:25:31 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2023-08-20 16:11:50 +00:00
|
|
|
#else
|
|
|
|
static const AuUInt32 kRef { 0 };
|
|
|
|
|
|
|
|
if (TryWaitOnAddress(&this->uAtomicState, &kRef, sizeof(kRef)))
|
|
|
|
{
|
|
|
|
return TryLock3();
|
|
|
|
}
|
|
|
|
#endif
|
2023-08-19 21:25:31 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auline bool SleepOne(AuUInt64 qwTimeout)
|
|
|
|
{
|
|
|
|
static const AuUInt32 kRef { 0 };
|
|
|
|
|
|
|
|
while (!TryLock2())
|
|
|
|
{
|
|
|
|
bool bStatus {};
|
|
|
|
|
|
|
|
bStatus = WaitOnAddressSteady((void *)&this->uAtomicState, &kRef, sizeof(kRef), qwTimeout);
|
|
|
|
|
|
|
|
if (!bStatus)
|
|
|
|
{
|
|
|
|
return TryLock2();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|