[*] Thread primitives optimization

[*] Fix wake on address regression
This commit is contained in:
Reece Wilson 2023-12-10 20:01:06 +00:00
parent 1a71a7dd41
commit 0747ff230e
9 changed files with 99 additions and 55 deletions

View File

@ -108,7 +108,13 @@ namespace Aurora::Threading
if (!this->bAlive)
{
#if !defined(WOA_SEMAPHORE_MODE)
this->mutex.Unlock();
#endif
(void)gProcessWaitables.WaitBufferFrom(this->pAddress, this->uSize);
#if !defined(WOA_SEMAPHORE_MODE)
this->mutex.Lock();
#endif
}
else
{
@ -116,7 +122,7 @@ namespace Aurora::Threading
this->semaphore->LockAbsNS(uEndTime);
#else
auto uTimeRemNS = uEndTime - uNow;
this->variable.WaitForSignalNsEx(&this->mutex, uTimeRemNS);
this->variable.WaitForSignalNsEx(&this->mutex, uTimeRemNS, false);
#endif
}
@ -131,14 +137,20 @@ namespace Aurora::Threading
{
if (!this->bAlive)
{
#if !defined(WOA_SEMAPHORE_MODE)
this->mutex.Unlock();
#endif
(void)gProcessWaitables.WaitBufferFrom(this->pAddress, this->uSize);
#if !defined(WOA_SEMAPHORE_MODE)
this->mutex.Lock();
#endif
}
else
{
#if defined(WOA_SEMAPHORE_MODE)
this->semaphore->Lock();
#else
this->variable.WaitForSignalNsEx(&this->mutex, 0);
this->variable.WaitForSignalNsEx(&this->mutex, 0, false);
#endif
}
}

View File

@ -168,7 +168,6 @@ namespace Aurora::Threading::Primitives
while (!this->TryLockNoSpin())
{
int ret {};
bool bStatus {};

View File

@ -24,7 +24,27 @@ namespace Aurora::Threading::Primitives
}
bool ConditionVariableLinux::WaitOne(AuUInt64 qwTimeoutRelative)
bool ConditionVariableLinux::TryTakeOneNoSpin()
{
auto old = this->uState_;
return old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old;
}
bool ConditionVariableLinux::TryTakeOneSpin()
{
if (ThrdCfg::gPreferLinuxPrimitivesFutexNoSpin)
{
return this->TryTakeOneNoSpin();
}
return DoTryIf([=]()
{
return this->TryTakeOneNoSpin();
});
}
bool ConditionVariableLinux::WaitOne(AuUInt64 qwTimeoutRelative,
bool bSpin)
{
AuUInt64 uStart {};
AuUInt64 uEnd {};
@ -38,11 +58,7 @@ namespace Aurora::Threading::Primitives
Time::monoabsns2ts(&tspec, uEnd);
}
if (DoTryIf([=]()
{
auto old = this->uState_;
return (old != 0 && AuAtomicCompareExchange(&this->uState_, old - 1, old) == old);
}))
if (bSpin ? this->TryTakeOneSpin() : this->TryTakeOneNoSpin())
{
return true;
}
@ -97,7 +113,8 @@ namespace Aurora::Threading::Primitives
}
bool ConditionVariableLinux::WaitForSignalNsEx(LinuxConditionMutex *pMutex,
AuUInt64 qwTimeoutRelative)
AuUInt64 qwTimeoutRelative,
bool bSpin)
{
AuAtomicAdd(&this->uSleeping_, 1u);
@ -107,7 +124,7 @@ namespace Aurora::Threading::Primitives
pMutex->Unlock();
}
auto bSuccess = this->WaitOne(qwTimeoutRelative);
auto bSuccess = this->WaitOne(qwTimeoutRelative, bSpin);
if (!bSuccess)
{
auto uWaiters = this->uSleeping_;

View File

@ -20,12 +20,15 @@ namespace Aurora::Threading::Primitives
ConditionVariableLinux();
~ConditionVariableLinux();
bool WaitForSignalNsEx(LinuxConditionMutex *pMutex, AuUInt64 timeout);
bool WaitForSignalNsEx(LinuxConditionMutex *pMutex, AuUInt64 timeout, bool bSpin = true);
void Signal();
void Broadcast();
bool WaitOne(AuUInt64 qwTimeout);
bool WaitOne(AuUInt64 qwTimeout, bool bSpin);
private:
bool TryTakeOneNoSpin();
bool TryTakeOneSpin();
AuUInt32 uState_ {};
AuUInt32 uSleeping_ {};
};

View File

@ -26,7 +26,7 @@ namespace Aurora::Threading::Primitives
#endif
}
bool ConditionVariableNT::WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 qwTimeout)
bool ConditionVariableNT::WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 qwTimeout, bool bSpin)
{
#if !defined(AURORA_FORCE_SRW_LOCKS)
bool bRet { true };
@ -34,9 +34,7 @@ namespace Aurora::Threading::Primitives
if (qwTimeout)
{
//#if defined(AU_TRUST_NT_KERNEL_SCHED_TIMEOUT)
auto uEndTimeSteady = gUseNativeWaitCondvar ? AuTime::SteadyClockNS() + qwTimeout : 0; // we could nuke this again, if i really wanted to
// #endif
auto uEndTimeSteady = gUseNativeWaitCondvar ? AuTime::SteadyClockNS() + qwTimeout : 0;
auto uEndTimeWall = AuTime::CurrentClockNS() + qwTimeout;
auto uTargetTimeNt = AuTime::ConvertTimestampNs(uEndTimeWall);
bool bIOU {};
@ -65,7 +63,8 @@ namespace Aurora::Threading::Primitives
if (gUseNativeWaitCondvar)
{
// Reverted: 5b495f7fd9495aa55395666e166ac499955215dc
if (ThrdCfg::gPreferNtCondvarModernWinSpin)
if (bSpin &&
ThrdCfg::gPreferNtCondvarModernWinSpin)
{
if (this->CheckOut())
{
@ -73,14 +72,19 @@ namespace Aurora::Threading::Primitives
return true;
}
}
else if (this->CheckOutNoSpin()) {
pMutex->Lock();
return true;
}
AuUInt8 uBlockBit { 1 };
bRet = InternalLTSWaitOnAddressHighRes(&this->wlist, &uBlockBit, 1, uEndTimeSteady);
}
else
{
// Reverted: 5b495f7fd9495aa55395666e166ac499955215dc
if (ThrdCfg::gPreferNtCondvarOlderWinSpin)
if (bSpin &&
ThrdCfg::gPreferNtCondvarOlderWinSpin)
{
if (!bIOU)
{
@ -135,7 +139,6 @@ namespace Aurora::Threading::Primitives
continue;
}
// go for an atomic decrement while racing against ::Signal and ::Broadcast
auto waiting = uOld - 1u;
auto uNext = waiting << kShiftCountByBits;
@ -150,8 +153,8 @@ namespace Aurora::Threading::Primitives
}
else
{
// we good?
if (bIOU || this->CheckOut())
if (bIOU ||
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
{
return true;
}
@ -179,7 +182,8 @@ namespace Aurora::Threading::Primitives
if (gUseNativeWaitCondvar)
{
if (ThrdCfg::gPreferNtCondvarModernWinSpin)
if (bSpin &&
ThrdCfg::gPreferNtCondvarModernWinSpin)
{
if (this->CheckOut())
{
@ -187,13 +191,19 @@ namespace Aurora::Threading::Primitives
return true;
}
}
else if (this->CheckOutNoSpin())
{
pMutex->Lock();
return true;
}
AuUInt8 uBlockBit { 1 };
bRet = InternalLTSWaitOnAddressHighRes(&this->wlist, &uBlockBit, 1, 0);
}
else
{
if (ThrdCfg::gPreferNtCondvarOlderWinSpin)
if (bSpin &&
ThrdCfg::gPreferNtCondvarOlderWinSpin)
{
if (!bIOU)
{
@ -207,7 +217,7 @@ namespace Aurora::Threading::Primitives
pMutex->Lock();
if (bIOU ||
this->CheckOutNoSpin())
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
{
return bRet;
}
@ -257,33 +267,33 @@ namespace Aurora::Threading::Primitives
#if defined(AURORA_FORCE_SRW_LOCKS)
return false;
#else
auto uSignalNow = this->signalCount;
AuUInt32 uSignalNow {};
AuUInt32 uSignalNext {};
if (uSignalNow == 0)
while (true)
{
return false;
}
uSignalNow = AuAtomicLoad(&this->signalCount);
auto uSignalNext = uSignalNow - 1;
if (AuAtomicCompareExchange(&this->signalCount, uSignalNext, uSignalNow) != uSignalNow)
{
return false;
}
if constexpr (kBoolRequiredLateSet)
{
if (uSignalNext == 0)
if (uSignalNow == 0)
{
InterlockedOr((volatile LONG *)&this->wlist, 1);
return false;
}
// paranoia
#if 1
if (AuAtomicLoad(&this->signalCount) != 0) [[unlikely]]
{
AuAtomicUnset(&this->wlist, 0);
}
#endif
uSignalNext = uSignalNow - 1;
if (AuAtomicCompareExchange(&this->signalCount, uSignalNext, uSignalNow) == uSignalNow)
{
break;
}
}
if (uSignalNext == 0)
{
InterlockedOr((volatile LONG *)&this->wlist, 1);
if (AuAtomicLoad(&this->signalCount) != 0) [[unlikely]]
{
AuAtomicUnset(&this->wlist, 0);
}
}

View File

@ -16,7 +16,7 @@ namespace Aurora::Threading::Primitives
{
ConditionVariableNT();
bool WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 timeout);
bool WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 timeout, bool bSpin = true);
void Signal();
void Broadcast();
void BroadcastN(AuUInt32 nBroadcast);

View File

@ -46,7 +46,8 @@ namespace Aurora::Threading::Primitives
}
bool ConditionVariableImpl::WaitForSignalNsEx(const std::shared_ptr<UnixConditionMutex> &pMutex,
AuUInt64 qwTimeout)
AuUInt64 qwTimeout,
bool bSpin)
{
auto mutex = reinterpret_cast<pthread_mutex_t*>(pMutex->GetOSHandle());

View File

@ -19,7 +19,9 @@ namespace Aurora::Threading::Primitives
AuSPtr<IConditionMutex> GetMutex() override;
bool WaitForSignal(AuUInt32 timeout) override;
bool WaitForSignalNsEx(const std::shared_ptr<UnixConditionMutex> &pMutex, AuUInt64 timeout);
bool WaitForSignalNsEx(const std::shared_ptr<UnixConditionMutex> &pMutex,
AuUInt64 qwTimeout,
bool bSpin = true);
bool WaitForSignalNS(AuUInt64 qwTimeout) override;
void Signal() override;
void Broadcast() override;

View File

@ -135,11 +135,11 @@ namespace Aurora::Threading::Primitives
return false;
}
var.WaitForSignalNsEx(&this->mutex, uEnd - uStart);
var.WaitForSignalNsEx(&this->mutex, uEnd - uStart, false);
}
else
{
var.WaitForSignalNsEx(&this->mutex, 0);
var.WaitForSignalNsEx(&this->mutex, 0, false);
}
}
@ -192,11 +192,11 @@ namespace Aurora::Threading::Primitives
return false;
}
var.WaitForSignalNsEx(&this->mutex, qwTimeoutAbs - uStart);
var.WaitForSignalNsEx(&this->mutex, qwTimeoutAbs - uStart, false);
}
else
{
var.WaitForSignalNsEx(&this->mutex, 0);
var.WaitForSignalNsEx(&this->mutex, 0, false);
}
}