AuroraRuntime/Source/Threading/AuWakeOnAddress.hpp
Jamie Reece Wilson ecd780b95f [*] WaitForMultipleAddresses fix:
this->waitList.pHead == pEntry, this->waitList.pTail == pEntry under remove head. These must take a slowpath instead
2024-11-30 02:10:52 +00:00

185 lines
7.2 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuWakeOnAddress.hpp
Date: 2023-3-10
Author: Reece
***/
#pragma once
#include "Primitives/AuWoASemaphore.hpp"
#include "Primitives/AuConditionMutex.Generic.hpp"
#include "Primitives/AuConditionVariable.Generic.hpp"
#include "Primitives/AuSemaphore.Generic.hpp"
#if defined(AURORA_COMPILER_MSVC)
#define WOAFAST __declspec(safebuffers) auline
#define WOAFASTPUB AUKN_SYM __declspec(safebuffers) auline
#else
#define WOAFAST auline
#define WOAFASTPUB AUKN_SYM
#endif
namespace Aurora::Threading
{
static const auto kDefaultWaitPerProcess = 128;
static const auto kMax64 = 0xFFFFFFFFFFFFFFFFull;
static const auto kPlatformFutexNoForcedAlignedU32 = AuBuild::kIsNTDerived;
struct WaitState;
struct MultipleInternalContext;
struct WaitMulipleContainer;
struct WaitBuffer
{
char buffer[32];
AuUInt8 uSize;
WOAFAST static WaitBuffer From(const void *pBuf, AuUInt8 uSize);
WOAFAST static bool Compare(const void *pHotAddress,
AuUInt8 uSize,
WaitState &state);
WOAFAST static bool Compare(const void *pHotAddress,
AuUInt8 uSize,
const void *pCompare,
AuUInt64 uMask,
EWaitMethod eMethod);
// returns false when valid
template <EWaitMethod eMethod, bool bFast = false>
WOAFAST static bool Compare2(const void *pHotAddress,
AuUInt8 uSize,
const void *pReference,
AuUInt64 uMask = 0xFFFFFFFFFFFFFFFF);
template <EWaitMethod eMethod, bool bFast = false>
WOAFAST static bool Compare2(const volatile void *pHotAddress,
AuUInt8 uSize,
const void *pReference,
AuUInt64 uMask = 0xFFFFFFFFFFFFFFFF);
};
struct WaitState
{
WaitBuffer compare;
//AuOptionalEx<AuUInt64> qwNanoseconds;
AuOptionalEx<AuUInt64> qwNanosecondsAbs;
AuUInt64 uDownsizeMask { 0xFFFFFFFFFFFFFFFF };
AuUInt32 uWordSize { };
const void * pCompare2 { };
EWaitMethod eWaitMethod { EWaitMethod::eNotEqual };
};
struct WaitEntry
{
WaitEntry();
~WaitEntry();
WaitEntry * volatile pNext {};
WaitEntry * volatile pBefore {};
const WaitMulipleContainer * volatile pSpecial {};
// synch
#if defined(WOA_SEMAPHORE_MODE)
#if !defined(WOA_SEMAPHORE_SEMAPHORE)
Primitives::Semaphore semaphore;
#else
// Recommended for XNU targets:
WOA_SEMAPHORE_SEMAPHORE semaphore;
#endif
#else
// Recommended (we can better filter spurious wakes for the cost of a barrier on signal):
// !!! we also prefer to block the containers mutex while we signal each thread individually !!!
// !!! for the sake of optimizing for windows xp - 7, its far nicer to optimize the entire signaling and wait operations under a container lock, than it is to buffer shared pointers or externally managed memory out of the lock scope !!!
// !!! also note: container spinlocks =/= WaitEntry::mutex !!
#if !defined(WOA_CONDVAR_MUTEX)
Primitives::ConditionMutexInternal mutex; // mutex ctor must come before var
Primitives::ConditionVariableInternal variable; // ...and something all 2007+ micro and monolithic kernels should have; an event or semaphore primitive on which we can form a crude condvar
#else
WOA_CONDVAR_MUTEX mutex;
WOA_CONDVAR_VARIABLE variable;
#endif
#endif
// state for the signal side [no multiple]
const void * pAddress {};
AuUInt8 uSize {};
const void * pCompareAddress {};
EWaitMethod eWaitMethod { EWaitMethod::eNotEqual };
// bookkeeping (parent container)
volatile AuUInt8 bAlive {}; // wait entry validity. must be rechecked for each spurious or expected wake, if the comparison doesn't break the yield loop.
// if false, and we're still yielding under pCompare == pAddress, we must reschedule with inverse order (as to steal the next signal, as opposed to waiting last)
void Release();
template <EWaitMethod eMethod>
bool SleepOn(WaitState &state);
bool SleepLossy(AuUInt64 qwNanosecondsAbs);
bool TrySignalAddress(const void *pAddress);
auline WaitEntry *GetNext(const void *pAddress);
auline WaitEntry *GetSimilarLastItr(const void *pAddress);
auline void SetNext(const void *pAddress, WaitEntry *pNext);
auline WaitEntry *GetBefore(const void *pAddress);
auline WaitEntry *GetSimilarFirstItr(const void *pAddress);
auline void SetBefore(const void *pAddress, WaitEntry *pNext);
};
struct ProcessListWait
{
WaitEntry *pHead {};
WaitEntry *pTail {};
};
struct ProcessWaitNodeContainer
{
AuUInt32 uAtomic {};
ProcessListWait waitList;
WaitEntry *WaitBufferFrom(const void *pAddress, AuUInt8 uSize, bool bScheduleFirst, const void *pAddressCompare, EWaitMethod eWaitMethod);
WaitEntry *WaitBufferFrom2(const void *pAddress, AuUInt8 uSize, const void *pAddressCompare, EWaitMethod eWaitMethod, MultipleInternalContext *pContext, const WaitMulipleContainer *pContainer);
template <typename T>
bool IterateWake(const void *pAddress, T callback);
void RemoveSelf(const void *pAddress, WaitEntry *pSelf);
template <bool bAllUnderLock>
void RemoveEntry(const void *pAddress, WaitEntry *pSelf);
void Lock();
void Unlock();
};
struct ProcessWaitContainer
{
ProcessWaitNodeContainer list[kDefaultWaitPerProcess];
WaitEntry *WaitBufferFrom(const void *pAddress, AuUInt8 uSize, bool bScheduleFirst, const void *pAddressCompare, EWaitMethod eWaitMethod);
WaitEntry *WaitBufferFrom2(const void *pAddress, AuUInt8 uSize, const void *pAddressCompare, EWaitMethod eWaitMethod, MultipleInternalContext *pContext, const WaitMulipleContainer *pContainer);
template <typename T>
bool IterateWake(const void *pAddress, T callback);
void RemoveSelf(const void *pAddress, WaitEntry *pSelf);
};
inline ProcessWaitContainer gProcessWaitables;
struct MultipleInternalContext
{
WaitState state;
WaitEntry * pBefore {};
WaitEntry * pNext {};
bool bOldIgnore {};
AuUInt16 uOldStateChangedCounter {};
};
}