2023-03-12 15:27:28 +00:00
|
|
|
/***
|
|
|
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
|
|
|
|
File: AuWakeOnAddress.cpp
|
|
|
|
Date: 2023-3-10
|
|
|
|
Author: Reece
|
|
|
|
***/
|
|
|
|
#include <Source/RuntimeInternal.hpp>
|
|
|
|
#include "AuWakeOnAddress.hpp"
|
2023-03-21 03:18:09 +00:00
|
|
|
#include "Primitives/SMTYield.hpp"
|
2023-05-08 14:16:06 +00:00
|
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
|
|
#include <timeapi.h>
|
|
|
|
#endif
|
2023-06-15 19:44:27 +00:00
|
|
|
#include <Time/Time.hpp>
|
2023-03-12 15:27:28 +00:00
|
|
|
|
2023-03-21 03:18:09 +00:00
|
|
|
namespace Aurora::Threading
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
static thread_local WaitEntry tlsWaitEntry;
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
static int futex_wait(uint32_t *addr, uint32_t expected, const struct timespec *timeout)
|
|
|
|
{
|
|
|
|
if (timeout)
|
|
|
|
{
|
|
|
|
return futex(addr, FUTEX_WAIT_BITSET, expected, timeout, 0, FUTEX_BITSET_MATCH_ANY);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int futex_wake(uint32_t *addr, uint32_t nthreads)
|
|
|
|
{
|
|
|
|
return futex(addr, FUTEX_WAKE, nthreads, 0, 0, 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static ProcessWaitContainer gProcessWaitables;
|
2023-06-11 18:13:37 +00:00
|
|
|
static int gShouldSpinOnlyInCPU = 1; // TODO: havent decided
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
static void DoSpinLockOnVar(T *uPointer)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
if (gShouldSpinOnlyInCPU == 0)
|
|
|
|
{
|
|
|
|
while (!Primitives::DoTryIf([&]()
|
|
|
|
{
|
|
|
|
return AuAtomicTestAndSet(uPointer, 0) == 0;
|
|
|
|
}))
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (gShouldSpinOnlyInCPU == 1)
|
|
|
|
{
|
|
|
|
while (!Primitives::DoTryIf([&]()
|
|
|
|
{
|
|
|
|
return AuAtomicTestAndSet(uPointer, 0) == 0;
|
|
|
|
}))
|
|
|
|
{
|
|
|
|
ContextYield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (gShouldSpinOnlyInCPU == 2)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
while (AuAtomicTestAndSet(uPointer, 0))
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
while (*uPointer)
|
|
|
|
{
|
|
|
|
ContextYield();
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-11 18:13:37 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
SysUnreachable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitEntry::TryAcquire(const void *pAddress, AuUInt8 uSize)
|
|
|
|
{
|
|
|
|
DoSpinLockOnVar(&this->uAtomic);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
//AU_LOCK_GUARD(this->mutex);
|
|
|
|
|
|
|
|
if (this->pAddress)
|
|
|
|
{
|
|
|
|
this->uAtomic = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->pAddress = pAddress;
|
|
|
|
this->uSize = uSize;
|
|
|
|
this->uAtomic = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitEntry::Release()
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if 0
|
2023-03-12 15:27:28 +00:00
|
|
|
if (this->bOverflow)
|
|
|
|
{
|
|
|
|
gProcessWaitables.Remove(this);
|
|
|
|
this->bOverflow = false;
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
AuResetMember(this->uSize);
|
|
|
|
AuResetMember(this->pAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
WaitEntry::WaitEntry() :
|
|
|
|
variable(AuUnsafeRaiiToShared(&this->mutex))
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
WaitEntry::~WaitEntry()
|
|
|
|
{
|
|
|
|
this->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitEntry::SleepOn(WaitState &state)
|
|
|
|
{
|
|
|
|
AU_LOCK_GUARD(this->mutex);
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
if (state.qwNanosecondsAbs)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
if (!WaitBuffer::From(this->pAddress, this->uSize).Compare(state))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
AuUInt64 uEndTime {};
|
2023-03-12 15:27:28 +00:00
|
|
|
auto uNow = AuTime::SteadyClockNS();
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
if (state.qwNanosecondsAbs)
|
|
|
|
{
|
|
|
|
uEndTime = state.qwNanosecondsAbs.value();
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
#if defined(AURORA_IS_POSIX_DERIVED)
|
|
|
|
struct timespec tspec;
|
|
|
|
Time::auabsns2ts(&tspec, uEndTime);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (uNow < uEndTime)
|
|
|
|
{
|
|
|
|
if (!WaitBuffer::From(this->pAddress, this->uSize).Compare(state))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto uTimeRemNS = uEndTime - uNow;
|
|
|
|
|
|
|
|
#if defined(AURORA_IS_POSIX_DERIVED)
|
|
|
|
|
|
|
|
auto pCond = reinterpret_cast<pthread_cond_t *>(&this->variable.pthreadCv_);
|
|
|
|
auto mutex = reinterpret_cast<pthread_mutex_t *>(this->mutex->GetOSHandle());
|
|
|
|
|
|
|
|
int ret {};
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = ::pthread_cond_timedwait(pCond, mutex, &tspec);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == ETIMEDOUT)
|
|
|
|
{
|
|
|
|
return !WaitBuffer::From(this->pAddress, this->uSize).Compare(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
while (ret == EINTR);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2023-05-08 14:16:06 +00:00
|
|
|
#if defined(AURORA_PLATFORM_WIN32)
|
|
|
|
if (::timeBeginPeriod(0) == TIMERR_NOCANDO)
|
|
|
|
{
|
|
|
|
::timeBeginPeriod(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-03-17 15:40:15 +00:00
|
|
|
this->variable.WaitForSignalNS(uTimeRemNS);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uNow = AuTime::SteadyClockNS();
|
|
|
|
}
|
|
|
|
|
|
|
|
return !WaitBuffer::From(this->pAddress, this->uSize).Compare(state);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (WaitBuffer::From(this->pAddress, this->uSize).Compare(state))
|
|
|
|
{
|
|
|
|
this->variable.WaitForSignal(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitEntry::TryWake(const void *pAddress)
|
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
DoSpinLockOnVar(&this->uAtomic);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
auto bRet = TryWakeNoLock(pAddress);
|
|
|
|
if (!bRet)
|
|
|
|
{
|
|
|
|
this->uAtomic = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitEntry::TryWakeNoLockNoReallyNoLock(const void *pAddress)
|
|
|
|
{
|
|
|
|
if (AuReinterpretCast<const char *>(this->pAddress) > AuReinterpretCast<const char *>(pAddress) ||
|
|
|
|
AuReinterpretCast<const char *>(this->pAddress) + this->uSize <= AuReinterpretCast<const char *>(pAddress))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->uAtomic = 0;
|
|
|
|
this->variable.Signal();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitEntry::TryWakeNoLock(const void *pAddress)
|
|
|
|
{
|
|
|
|
if (AuReinterpretCast<const char *>(this->pAddress) > AuReinterpretCast<const char *>(pAddress) ||
|
|
|
|
AuReinterpretCast<const char *>(this->pAddress) + this->uSize <= AuReinterpretCast<const char *>(pAddress))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
AU_LOCK_GUARD(this->mutex);
|
|
|
|
this->uAtomic = 0;
|
|
|
|
this->variable.Signal();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
WaitBuffer WaitBuffer::From(const void *pBuf, AuUInt8 uSize)
|
|
|
|
{
|
|
|
|
WaitBuffer wait;
|
|
|
|
AuMemcpy(wait.buffer, pBuf, uSize);
|
|
|
|
wait.uSize = uSize;
|
|
|
|
return AuMove(wait);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitBuffer::Compare(const void *pBuf)
|
|
|
|
{
|
|
|
|
return AuMemcmp(this->buffer, pBuf, this->uSize) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitBuffer::Compare(WaitState &state)
|
|
|
|
{
|
|
|
|
if (!state.uDownsizeMask)
|
|
|
|
{
|
|
|
|
return AuMemcmp(this->buffer, state.compare.buffer, AuMin(this->uSize, state.compare.uSize)) == 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto uMask = state.uDownsizeMask.value();
|
|
|
|
|
|
|
|
auto &uSrcWord = *AuReinterpretCast<AuUInt32 *>(this->buffer);
|
|
|
|
auto &uCmpWord = *AuReinterpretCast<AuUInt32 *>(state.compare.buffer);
|
|
|
|
|
|
|
|
return (uSrcWord & uMask) == (uCmpWord & uMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-11 18:13:37 +00:00
|
|
|
WaitEntry *ProcessWaitContainer::WaitBufferFrom(void *pAddress, AuUInt8 uSize)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_ENABLE_OLD_SHORT_LIST)
|
2023-03-12 15:27:28 +00:00
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
|
|
|
{
|
|
|
|
if (this->entries[i].TryAcquire(pAddress, uSize))
|
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
return &this->entries[i];
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-06-11 18:13:37 +00:00
|
|
|
|
|
|
|
auto pReturn = &tlsWaitEntry;
|
2023-06-12 17:31:10 +00:00
|
|
|
pReturn->bReleaseOnWake = true;
|
|
|
|
|
|
|
|
pReturn->pAddress = pAddress;
|
|
|
|
pReturn->uSize = uSize;
|
|
|
|
pReturn->uAtomic = 0;
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
Lock();
|
2023-06-12 17:31:10 +00:00
|
|
|
if (auto pLoadFromMemory = this->waitList.pHead)
|
|
|
|
{
|
|
|
|
pReturn->pNext = pLoadFromMemory;
|
|
|
|
pLoadFromMemory->pBefore = pReturn;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this->waitList.pTail = pReturn;
|
|
|
|
}
|
|
|
|
this->waitList.pHead = pReturn;
|
2023-03-12 15:27:28 +00:00
|
|
|
Unlock();
|
|
|
|
}
|
|
|
|
|
2023-06-11 18:13:37 +00:00
|
|
|
return pReturn;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool ProcessWaitContainer::IterateAll(T callback)
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_ENABLE_OLD_SHORT_LIST)
|
2023-03-12 15:27:28 +00:00
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
|
|
|
{
|
|
|
|
auto &entry = this->entries[i];
|
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
DoSpinLockOnVar(&entry.uAtomic);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
2023-06-11 18:13:37 +00:00
|
|
|
if (entry.pAddress)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
// Intentional lock/unlock order:
|
2023-03-12 15:27:28 +00:00
|
|
|
AU_LOCK_GUARD(entry.mutex);
|
|
|
|
entry.uAtomic = 0;
|
2023-06-11 18:13:37 +00:00
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
if (!callback(entry))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2023-06-11 18:13:37 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
entry.uAtomic = 0;
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
Lock();
|
|
|
|
auto pCurrentHead = this->waitList.pHead;
|
|
|
|
while (pCurrentHead)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
AU_LOCK_GUARD(pCurrentHead->mutex);
|
|
|
|
|
|
|
|
if (!callback(pCurrentHead))
|
|
|
|
{
|
|
|
|
Unlock();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
pCurrentHead = pCurrentHead->pNext;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
Unlock();
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool ProcessWaitContainer::IterateForceNoCreateDuringOp(T callback)
|
|
|
|
{
|
|
|
|
bool bRetStatus { true };
|
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_ENABLE_OLD_SHORT_LIST)
|
2023-03-12 15:27:28 +00:00
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
|
|
|
{
|
|
|
|
auto &entry = this->entries[i];
|
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
DoSpinLockOnVar(&entry.uAtomic);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
if (entry.pAddress)
|
|
|
|
{
|
|
|
|
AU_LOCK_GUARD(entry.mutex);
|
|
|
|
if (!callback(entry))
|
|
|
|
{
|
|
|
|
for (AU_ITERATE_N(z, i + 1))
|
|
|
|
{
|
|
|
|
this->entries[z].uAtomic = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
auto pCurrentHead = this->waitList.pHead;
|
|
|
|
while (pCurrentHead)
|
|
|
|
{
|
|
|
|
AU_LOCK_GUARD(pCurrentHead->mutex);
|
|
|
|
|
|
|
|
if (!callback(*pCurrentHead))
|
|
|
|
{
|
|
|
|
bRetStatus = false;
|
|
|
|
break;
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
pCurrentHead = pCurrentHead->pNext;
|
|
|
|
}
|
|
|
|
Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(WOA_ENABLE_OLD_SHORT_LIST)
|
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
auto &entry = this->entries[i];
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
entry.uAtomic = 0;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return bRetStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool ProcessWaitContainer::IterateWake(T callback)
|
|
|
|
{
|
|
|
|
bool bRetStatus { true };
|
|
|
|
|
|
|
|
Lock();
|
|
|
|
{
|
|
|
|
// FIFO
|
|
|
|
auto pCurrentHead = this->waitList.pTail;
|
|
|
|
decltype(pCurrentHead) pLast {};
|
|
|
|
while (pCurrentHead)
|
|
|
|
{
|
|
|
|
AU_LOCK_GUARD(pCurrentHead->mutex);
|
|
|
|
|
|
|
|
auto [bCont, bRemove] = callback(*pCurrentHead);
|
|
|
|
|
|
|
|
if (bRemove)
|
|
|
|
{
|
|
|
|
if (pLast)
|
|
|
|
{
|
|
|
|
pLast->pNext = pCurrentHead->pNext;
|
|
|
|
}
|
2023-06-12 18:12:13 +00:00
|
|
|
|
|
|
|
if (this->waitList.pHead == pCurrentHead)
|
2023-06-12 17:31:10 +00:00
|
|
|
{
|
|
|
|
this->waitList.pHead = pCurrentHead->pNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCurrentHead->pNext)
|
|
|
|
{
|
|
|
|
pCurrentHead->pNext->pBefore = pCurrentHead->pBefore;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->waitList.pTail == pCurrentHead)
|
|
|
|
{
|
2023-06-12 18:12:13 +00:00
|
|
|
this->waitList.pTail = pCurrentHead->pBefore;
|
2023-06-12 17:31:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bCont)
|
|
|
|
{
|
|
|
|
bRetStatus = false;
|
|
|
|
break;
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
pLast = pCurrentHead;
|
|
|
|
pCurrentHead = pCurrentHead->pBefore;
|
|
|
|
}
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
Unlock();
|
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
// meh - just so i can experiment with changes
|
|
|
|
#if defined(WOA_ENABLE_OLD_SHORT_LIST)
|
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
|
|
|
{
|
|
|
|
auto &entry = this->entries[i];
|
|
|
|
{
|
|
|
|
DoSpinLockOnVar(&entry.uAtomic);
|
|
|
|
|
|
|
|
if (entry.pAddress)
|
|
|
|
{
|
|
|
|
AU_LOCK_GUARD(entry.mutex);
|
|
|
|
|
|
|
|
auto [bCont, bRemove] = callback(*entry);
|
|
|
|
|
|
|
|
if (!bCont)
|
|
|
|
{
|
|
|
|
for (AU_ITERATE_N(z, i + 1))
|
|
|
|
{
|
|
|
|
this->entries[z].uAtomic = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
for (AU_ITERATE_N(i, kDefaultWaitPerProcess))
|
|
|
|
{
|
|
|
|
auto &entry = this->entries[i];
|
|
|
|
{
|
|
|
|
entry.uAtomic = 0;
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
return bRetStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessWaitContainer::Lock()
|
|
|
|
{
|
2023-06-11 18:13:37 +00:00
|
|
|
DoSpinLockOnVar(&this->uAtomic);
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessWaitContainer::Unlock()
|
|
|
|
{
|
|
|
|
this->uAtomic = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessWaitContainer::Remove(WaitEntry *pParent)
|
|
|
|
{
|
|
|
|
Lock();
|
|
|
|
{
|
2023-06-12 18:12:13 +00:00
|
|
|
auto pCurrent = this->waitList.pTail;
|
2023-06-12 17:31:10 +00:00
|
|
|
decltype(pCurrent) pLast {};
|
|
|
|
while (pCurrent)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
if (pCurrent == pParent)
|
|
|
|
{
|
|
|
|
if (pLast)
|
|
|
|
{
|
|
|
|
pLast->pNext = pCurrent->pNext;
|
|
|
|
}
|
|
|
|
else if (this->waitList.pHead == pCurrent)
|
|
|
|
{
|
|
|
|
this->waitList.pHead = pCurrent->pNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCurrent->pNext)
|
|
|
|
{
|
|
|
|
pCurrent->pNext->pBefore = pCurrent->pBefore;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->waitList.pTail == pParent)
|
|
|
|
{
|
|
|
|
this->waitList.pTail = pLast;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pLast = pCurrent;
|
2023-06-12 18:12:13 +00:00
|
|
|
pCurrent = pCurrent->pBefore;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
AUKN_SYM bool IsWaitOnRecommended()
|
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
|
|
|
return pWaitOnAddress &&
|
|
|
|
AuSwInfo::IsWindows8Point1OrGreater();
|
|
|
|
#elif defined(AURORA_PLATFORM_LINUX)
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @deprecated
|
|
|
|
AUKN_SYM const AuList<AuUInt8> &GetValidWordSizes()
|
|
|
|
{
|
|
|
|
static const AuList<AuUInt8> kArray =
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
|
|
|
{ 1, 2, 4, 8 };
|
|
|
|
#else
|
|
|
|
{ 4 };
|
|
|
|
#endif
|
|
|
|
return kArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaitOnAddressWide(void *pTargetAddress,
|
|
|
|
void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize,
|
|
|
|
AuOptional<AuUInt64> qwNanoseconds,
|
2023-06-15 19:44:27 +00:00
|
|
|
AuOptional<AuUInt64> qwNanosecondsAbs,
|
2023-03-12 15:27:28 +00:00
|
|
|
bool bOSSupportsWait
|
|
|
|
)
|
|
|
|
{
|
|
|
|
WaitState state;
|
2023-04-06 21:05:45 +00:00
|
|
|
SysAssertDbg(uWordSize <= 8);
|
2023-03-12 15:27:28 +00:00
|
|
|
auto pWaitEntry = gProcessWaitables.WaitBufferFrom(pTargetAddress, uWordSize);
|
|
|
|
state.compare = WaitBuffer::From(pCompareAddress, uWordSize);
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
if (qwNanoseconds)
|
|
|
|
{
|
|
|
|
state.qwNanosecondsAbs = AuTime::SteadyClockNS() + qwNanoseconds.value();
|
|
|
|
}
|
|
|
|
else if (qwNanosecondsAbs)
|
|
|
|
{
|
|
|
|
state.qwNanosecondsAbs = qwNanosecondsAbs.value();
|
|
|
|
}
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
auto bResult = pWaitEntry->SleepOn(state);
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
2023-03-12 15:27:28 +00:00
|
|
|
pWaitEntry->Release();
|
2023-06-12 17:31:10 +00:00
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
return bResult;
|
|
|
|
}
|
|
|
|
|
2023-03-13 23:57:32 +00:00
|
|
|
AuTuple<const void *, AuUInt8, AuOptionalEx<AuUInt32>> DecodeAddress(const void *pAddress,
|
2023-04-16 22:58:27 +00:00
|
|
|
AuUInt32 uWordSize)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
2023-03-13 23:57:32 +00:00
|
|
|
return AuMakeTuple(pAddress, 0, AuOptionalEx<AuUInt32> {});
|
2023-03-12 15:27:28 +00:00
|
|
|
#endif
|
|
|
|
|
2023-04-16 22:58:27 +00:00
|
|
|
if (uWordSize == 8)
|
|
|
|
{
|
|
|
|
return AuMakeTuple(pAddress, 0xFFFFFFFF, 0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
auto pRounded = AuPageRound(AuUInt(pAddress), AuUInt(4));
|
|
|
|
auto uDelta = (AuUInt)(pAddress) - (AuUInt)(pRounded);
|
2023-04-16 22:58:27 +00:00
|
|
|
|
|
|
|
AuUInt32 uSizeMask = std::pow(AuUInt64(2), AuUInt64(uWordSize * 8)) - 1ull;
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
switch (uDelta)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return AuMakeTuple(pAddress, 0, 0xFFFFFFFF & (uSizeMask << 0));
|
|
|
|
case 1:
|
|
|
|
return AuMakeTuple(pAddress, 1, 0xFFFFFF00 & (uSizeMask << 8));
|
|
|
|
case 2:
|
|
|
|
return AuMakeTuple(pAddress, 2, 0xFFFF0000 & (uSizeMask << 16));
|
|
|
|
case 3:
|
|
|
|
return AuMakeTuple(pAddress, 3, 0xFF000000 & (uSizeMask << 24));
|
|
|
|
default:
|
|
|
|
SysPanic("Invalid Branch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool RunOSWaitOnAddressNoTimed(const void *pTargetAddress,
|
|
|
|
const void *pCompareAddress,
|
|
|
|
AuUInt8 dwWordSize)
|
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
2023-06-15 23:10:53 +00:00
|
|
|
return pWaitOnAddress((void *)pTargetAddress, (void *)pCompareAddress, dwWordSize, INFINITE);
|
2023-03-12 15:27:28 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
int ret {};
|
|
|
|
|
2023-03-22 15:19:32 +00:00
|
|
|
#if defined(AU_CPU_ENDIAN_BIG)
|
2023-03-12 15:27:28 +00:00
|
|
|
if (dwWordSize == 8)
|
|
|
|
{
|
|
|
|
pTargetAddress = AuReinterpretCast<const char *>(pTargetAddress) + 4;
|
|
|
|
pCompareAddress = AuReinterpretCast<const char *>(pCompareAddress) + 4;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
auto uCurrent = *(AuUInt32 *)pCompareAddress;
|
2023-06-11 16:52:50 +00:00
|
|
|
auto expect = WaitBuffer::From(pCompareAddress, uWordSize);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = futex_wait((AuUInt32 *)pTargetAddress, uCurrent, nullptr);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == EAGAIN || errno == EAGAIN)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == ETIMEDOUT || errno == ETIMEDOUT)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (ret == EINTR);
|
|
|
|
|
2023-06-11 16:52:50 +00:00
|
|
|
return !expect.Compare(pTargetAddress);
|
2023-03-12 15:27:28 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool RunOSWaitOnAddressTimed(const void *pTargetAddress,
|
|
|
|
const void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize,
|
2023-06-15 19:44:27 +00:00
|
|
|
AuUInt64 uAbsTimeSteadyClock,
|
|
|
|
AuUInt64 uRelativeNanoseconds,
|
|
|
|
AuOptional<AuUInt64> uAbsTimeAltClock /* hint */)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
if (pRtlWaitOnAddress)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
auto expect = WaitBuffer::From(pCompareAddress, uWordSize);
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
AuUInt64 uNow {};
|
|
|
|
while (uAbsTimeSteadyClock ?
|
|
|
|
(uAbsTimeSteadyClock > (uNow = AuTime::SteadyClockNS())) :
|
|
|
|
true)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
LARGE_INTEGER word {};
|
|
|
|
|
|
|
|
if (uAbsTimeAltClock)
|
|
|
|
{
|
|
|
|
word.QuadPart = AuTime::ConvertTimestampNs(uAbsTimeAltClock.value());
|
|
|
|
}
|
|
|
|
else if (uAbsTimeSteadyClock)
|
|
|
|
{
|
|
|
|
if (uAbsTimeSteadyClock <= uNow)
|
|
|
|
{
|
|
|
|
return !WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
word.QuadPart = -(AuInt64(uAbsTimeSteadyClock - uNow) / 100ull);
|
2023-06-15 20:15:58 +00:00
|
|
|
|
|
|
|
if (!word.QuadPart)
|
|
|
|
{
|
|
|
|
word.QuadPart = 1;
|
|
|
|
}
|
2023-06-15 19:44:27 +00:00
|
|
|
}
|
2023-06-15 23:10:53 +00:00
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
if (expect.Compare(pTargetAddress))
|
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
pRtlWaitOnAddress(pTargetAddress, pCompareAddress, uWordSize, &word);
|
|
|
|
if (!expect.Compare(pTargetAddress))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (!uAbsTimeSteadyClock)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return true;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
return false;
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
// ~~some paths might miss the uRelativeNanoseconds, like cas loops.~~
|
|
|
|
// most paths will now skimp on the relative values
|
|
|
|
if (uAbsTimeSteadyClock && !uRelativeNanoseconds)
|
2023-03-13 23:57:32 +00:00
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
AuInt64 iDelta = uAbsTimeSteadyClock;
|
|
|
|
iDelta -= AuTime::SteadyClockNS();
|
|
|
|
|
|
|
|
if (iDelta <= 0)
|
|
|
|
{
|
|
|
|
return !WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress);
|
|
|
|
}
|
|
|
|
|
|
|
|
uRelativeNanoseconds = iDelta;
|
2023-03-13 23:57:32 +00:00
|
|
|
}
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
// LockN(<1MS) on a platform without that resolution of yielding... damn
|
|
|
|
auto uMS = AuNSToMS<AuUInt32>(uRelativeNanoseconds);
|
|
|
|
if (!uMS)
|
2023-03-13 23:57:32 +00:00
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
// take a copy
|
|
|
|
auto expect = WaitBuffer::From(pCompareAddress, uWordSize);
|
2023-03-13 23:57:32 +00:00
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
// first: cpu spin to avoid the kernel all together
|
|
|
|
if (TryWaitOnAddress((void *)pTargetAddress, (void *)pCompareAddress, uWordSize))
|
2023-03-13 23:57:32 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2023-06-15 19:44:27 +00:00
|
|
|
|
|
|
|
// second: yield
|
|
|
|
do
|
2023-03-13 23:57:32 +00:00
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
if (!expect.Compare(pTargetAddress))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-03-13 23:57:32 +00:00
|
|
|
AuThreading::ContextYield();
|
|
|
|
}
|
2023-06-15 19:44:27 +00:00
|
|
|
while (uAbsTimeSteadyClock > AuTime::SteadyClockNS()); // ...until times up
|
|
|
|
}
|
|
|
|
else // high level lock function was called with ms scale resolution
|
|
|
|
{
|
|
|
|
// first: wait on the address with an ms scale timeout
|
|
|
|
(void)pWaitOnAddress((void *)pTargetAddress, (void *)pCompareAddress, uWordSize, uMS);
|
|
|
|
|
|
|
|
// never trust the error value/status provided by wait addresses - instead, do a quick compare
|
|
|
|
if (!WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress))
|
2023-03-13 23:57:32 +00:00
|
|
|
{
|
2023-06-15 19:44:27 +00:00
|
|
|
// best case: we woke up during the ms-res waitonaddress
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// attempt to yield again, potentially context switching a few times to hit any NS remainder
|
|
|
|
AuUInt64 uNow {};
|
|
|
|
unsigned uLimit {};
|
|
|
|
while (uAbsTimeSteadyClock > (uNow = AuTime::SteadyClockNS()))
|
|
|
|
{
|
|
|
|
uMS = AuNSToMS<AuUInt32>(uAbsTimeSteadyClock - uNow);
|
|
|
|
|
|
|
|
if (Primitives::DoTryIf([=]()
|
|
|
|
{
|
|
|
|
return !WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress);
|
|
|
|
}))
|
|
|
|
{
|
|
|
|
// hit it within the span of 1 << SpinLoopPowerA SMT stalls
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uMS)
|
|
|
|
{
|
|
|
|
// burn off any remainder cycles by switching contexts (this isnt a very long time usually)
|
|
|
|
if (uLimit++ < 4)
|
|
|
|
{
|
|
|
|
AuThreading::ContextYield();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// do not burn the cpu to meet the timeout. we'll just undershoot.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(void)pWaitOnAddress((void *)pTargetAddress, (void *)pCompareAddress, uWordSize, uMS);
|
|
|
|
}
|
2023-03-13 23:57:32 +00:00
|
|
|
}
|
|
|
|
}
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
int ret {};
|
|
|
|
|
2023-03-22 15:19:32 +00:00
|
|
|
#if defined(AU_CPU_ENDIAN_BIG)
|
2023-03-12 15:27:28 +00:00
|
|
|
if (uWordSize == 8)
|
|
|
|
{
|
|
|
|
pTargetAddress = AuReinterpretCast<const char *>(pTargetAddress) + 4;
|
|
|
|
pCompareAddress = AuReinterpretCast<const char *>(pCompareAddress) + 4;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
auto uCurrent = *(AuUInt32 *)pCompareAddress;
|
|
|
|
|
|
|
|
struct timespec tspec;
|
2023-06-15 19:44:27 +00:00
|
|
|
Time::auabsns2ts(&tspec, uAbsTimeAltClock ? uAbsTimeAltClock.value() : uAbsTimeSteadyClock);
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = futex_wait((AuUInt32 *)pTargetAddress, uCurrent, &tspec);
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == EAGAIN || errno == EAGAIN)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == ETIMEDOUT || errno == ETIMEDOUT)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (ret == EINTR);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2023-03-13 23:57:32 +00:00
|
|
|
return !WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress);
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RunOSWaitOnAddressNoTimedNoErrors(const void *pTargetAddress,
|
|
|
|
const void *pCompareAddress,
|
2023-03-13 23:57:32 +00:00
|
|
|
WaitState &state)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-03-13 23:57:32 +00:00
|
|
|
while (WaitBuffer::From(pTargetAddress, state.uWordSize).Compare(state))
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-03-13 23:57:32 +00:00
|
|
|
if (!RunOSWaitOnAddressNoTimed(pTargetAddress, pCompareAddress, state.uWordSize))
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
AuThreading::ContextYield();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
static bool RunOSWaitOnAddressTimedSteady(const void *pTargetAddress,
|
|
|
|
const void *pCompareAddress,
|
|
|
|
WaitState &state)
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-03-13 23:57:32 +00:00
|
|
|
if (!WaitBuffer::From(pTargetAddress, state.uWordSize).Compare(state))
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-06-15 23:10:53 +00:00
|
|
|
(void)RunOSWaitOnAddressTimed(pTargetAddress, pCompareAddress, state.uWordSize, state.qwNanosecondsAbs.value(), { }, { });
|
2023-03-13 23:57:32 +00:00
|
|
|
return !WaitBuffer::From(pTargetAddress, state.uWordSize).Compare(state);
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void RunOSWakeNOnAddress(const void *pAddress,
|
|
|
|
AuUInt32 dwCount)
|
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
futex_wake((AuUInt32 *)pAddress, dwCount);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
|
|
|
for (AuUInt i = 0; i < dwCount; i++)
|
|
|
|
{
|
|
|
|
pWakeByAddressSingle((void *)pAddress);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RunOSWakeAllOnAddress(const void *pAddress)
|
|
|
|
{
|
|
|
|
#if defined(AURORA_IS_LINUX_DERIVED)
|
|
|
|
futex_wake((AuUInt32 *)pAddress, INT_MAX);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
|
|
|
pWakeByAddressAll((void *)pAddress);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
// Windows 8+ thread primitives might use me instead of the public API
|
|
|
|
// it does work on Linux and Windows 8+
|
|
|
|
// it does not, however, work on emulated platforms
|
|
|
|
// this is intentional
|
|
|
|
bool InternalLTSWaitOnAddressHighRes(void *pTargetAddress,
|
|
|
|
void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize,
|
|
|
|
AuUInt64 qwNanosecondsAbs)
|
|
|
|
{
|
|
|
|
auto [pWaitAddress, uDelta, uMask] = DecodeAddress(pTargetAddress, uWordSize);
|
|
|
|
auto pCompareAddress2 = AuReinterpretCast<char *>(pCompareAddress) - uDelta;
|
|
|
|
|
|
|
|
WaitState state;
|
|
|
|
state.uDownsizeMask = uMask;
|
|
|
|
state.compare = uMask ?
|
|
|
|
WaitBuffer::From(pCompareAddress2, 4) :
|
|
|
|
WaitBuffer::From(pCompareAddress2, uWordSize);
|
|
|
|
state.uWordSize = uMask ? 4 : uWordSize;
|
|
|
|
|
|
|
|
if (!qwNanosecondsAbs)
|
|
|
|
{
|
|
|
|
RunOSWaitOnAddressNoTimedNoErrors(pWaitAddress, pCompareAddress2, state);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state.qwNanosecondsAbs = qwNanosecondsAbs;
|
|
|
|
return RunOSWaitOnAddressTimedSteady(pWaitAddress, pCompareAddress2, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-12 15:27:28 +00:00
|
|
|
AUKN_SYM bool WaitOnAddress(void *pTargetAddress,
|
|
|
|
void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize,
|
|
|
|
AuUInt64 qwNanoseconds)
|
|
|
|
{
|
|
|
|
bool bWaitOnAddress = IsWaitOnRecommended();
|
|
|
|
if (bWaitOnAddress)
|
|
|
|
{
|
|
|
|
auto [pWaitAddress, uDelta, uMask] = DecodeAddress(pTargetAddress, uWordSize);
|
|
|
|
auto pCompareAddress2 = AuReinterpretCast<char *>(pCompareAddress) - uDelta;
|
2023-03-13 23:57:32 +00:00
|
|
|
|
|
|
|
WaitState state;
|
|
|
|
state.uDownsizeMask = uMask;
|
|
|
|
state.compare = uMask ?
|
|
|
|
WaitBuffer::From(pCompareAddress2, 4) :
|
|
|
|
WaitBuffer::From(pCompareAddress2, uWordSize);
|
|
|
|
state.uWordSize = uMask ? 4 : uWordSize;
|
2023-03-12 15:27:28 +00:00
|
|
|
|
|
|
|
if (!qwNanoseconds)
|
|
|
|
{
|
2023-03-13 23:57:32 +00:00
|
|
|
RunOSWaitOnAddressNoTimedNoErrors(pWaitAddress, pCompareAddress2, state);
|
2023-03-12 15:27:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-15 23:10:53 +00:00
|
|
|
state.qwNanosecondsAbs = qwNanoseconds + AuTime::SteadyClockNS();
|
2023-06-15 19:44:27 +00:00
|
|
|
return RunOSWaitOnAddressTimedSteady(pWaitAddress, pCompareAddress2, state);
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-05 08:42:48 +00:00
|
|
|
if (TryWaitOnAddress(pTargetAddress, pCompareAddress, uWordSize))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
return WaitOnAddressWide(pTargetAddress, pCompareAddress, uWordSize, qwNanoseconds, {}, false);
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
AUKN_SYM bool TryWaitOnAddress(void *pTargetAddress,
|
|
|
|
void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize)
|
|
|
|
{
|
2023-03-13 23:57:32 +00:00
|
|
|
return Primitives::DoTryIf([=]()
|
|
|
|
{
|
|
|
|
return !WaitBuffer::From(pCompareAddress, uWordSize).Compare(pTargetAddress);
|
|
|
|
});
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AUKN_SYM void WakeNOnAddress(void *pTargetAddress,
|
|
|
|
AuUInt8 uNMaximumThreads)
|
|
|
|
{
|
|
|
|
if (IsWaitOnRecommended())
|
|
|
|
{
|
|
|
|
RunOSWakeNOnAddress(pTargetAddress, uNMaximumThreads);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
2023-03-12 15:27:28 +00:00
|
|
|
(void)gProcessWaitables.IterateForceNoCreateDuringOp([&](WaitEntry &entry) -> bool
|
2023-06-12 17:31:10 +00:00
|
|
|
#else
|
|
|
|
(void)gProcessWaitables.IterateWake([&](WaitEntry &entry) -> AuPair<bool, bool>
|
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
|
|
|
if (!uNMaximumThreads)
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
2023-03-12 15:27:28 +00:00
|
|
|
return false;
|
2023-06-12 17:31:10 +00:00
|
|
|
#else
|
|
|
|
return AuMakePair(false, false);
|
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
bool bWake {};
|
2023-03-12 15:27:28 +00:00
|
|
|
if (entry.TryWakeNoLockNoReallyNoLock(pTargetAddress))
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
bWake = true;
|
2023-03-12 15:27:28 +00:00
|
|
|
uNMaximumThreads--;
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
bool bCont = uNMaximumThreads != 0;
|
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
|
|
|
return bCont;
|
|
|
|
#else
|
|
|
|
return AuMakePair(bCont, bWake);
|
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AUKN_SYM void WakeOnAddress(void *pTargetAddress)
|
|
|
|
{
|
|
|
|
WakeNOnAddress(pTargetAddress, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
AUKN_SYM void WakeAllOnAddress(void *pTargetAddress)
|
|
|
|
{
|
|
|
|
if (IsWaitOnRecommended())
|
|
|
|
{
|
|
|
|
RunOSWakeAllOnAddress(pTargetAddress);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
|
|
|
(void)gProcessWaitables.IterateForceNoCreateDuringOp([&](WaitEntry &entry) -> bool
|
|
|
|
#else
|
|
|
|
(void)gProcessWaitables.IterateWake([&](WaitEntry &entry) -> AuPair<bool, bool>
|
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
{
|
2023-06-12 17:31:10 +00:00
|
|
|
#if defined(WOA_USE_DEFERRED_REL)
|
2023-03-12 15:27:28 +00:00
|
|
|
entry.TryWakeNoLockNoReallyNoLock(pTargetAddress);
|
|
|
|
return true;
|
2023-06-12 17:31:10 +00:00
|
|
|
#else
|
|
|
|
return AuMakePair(true, entry.TryWakeNoLockNoReallyNoLock(pTargetAddress));
|
|
|
|
#endif
|
2023-03-12 15:27:28 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2023-06-12 17:31:10 +00:00
|
|
|
|
2023-06-15 19:44:27 +00:00
|
|
|
AUKN_SYM bool WaitOnAddressSteady(void *pTargetAddress,
|
|
|
|
void *pCompareAddress,
|
|
|
|
AuUInt8 uWordSize,
|
|
|
|
AuUInt64 qwNanoseconds)
|
|
|
|
{
|
|
|
|
bool bWaitOnAddress = IsWaitOnRecommended();
|
|
|
|
if (bWaitOnAddress)
|
|
|
|
{
|
|
|
|
auto [pWaitAddress, uDelta, uMask] = DecodeAddress(pTargetAddress, uWordSize);
|
|
|
|
auto pCompareAddress2 = AuReinterpretCast<char *>(pCompareAddress) - uDelta;
|
|
|
|
|
|
|
|
WaitState state;
|
|
|
|
state.uDownsizeMask = uMask;
|
|
|
|
state.compare = uMask ?
|
|
|
|
WaitBuffer::From(pCompareAddress2, 4) :
|
|
|
|
WaitBuffer::From(pCompareAddress2, uWordSize);
|
|
|
|
state.uWordSize = uMask ? 4 : uWordSize;
|
|
|
|
|
|
|
|
if (!qwNanoseconds)
|
|
|
|
{
|
|
|
|
RunOSWaitOnAddressNoTimedNoErrors(pWaitAddress, pCompareAddress2, state);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
state.qwNanosecondsAbs = qwNanoseconds;
|
|
|
|
return RunOSWaitOnAddressTimedSteady(pWaitAddress, pCompareAddress2, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (TryWaitOnAddress(pTargetAddress, pCompareAddress, uWordSize))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return WaitOnAddressWide(pTargetAddress, pCompareAddress, uWordSize, {}, qwNanoseconds, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-12 17:31:10 +00:00
|
|
|
// Future (Reece): AuThread aware (safe force-terminate)
|
|
|
|
// There are three ways we can go about this:
|
|
|
|
// Shared pointers
|
|
|
|
// Shared pointers such that we dont need to remove the raw pointer optimization
|
|
|
|
// Callback on thread death
|
|
|
|
//
|
|
|
|
// 1st would increase overhead for a case i dont want to condone
|
|
|
|
// 2nd would work but would probably require a callback on death
|
|
|
|
// 3rd would work.
|
|
|
|
//
|
|
|
|
// to be addressed later
|
2023-03-12 15:27:28 +00:00
|
|
|
}
|