Jamie Reece Wilson
ecd780b95f
this->waitList.pHead == pEntry, this->waitList.pTail == pEntry under remove head. These must take a slowpath instead
185 lines
7.2 KiB
C++
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 {};
|
|
};
|
|
} |