[*] Harden Windows XP - 7 condvar; where signalers can lock up pending scheduling n threads whilst a spurious wake is occurring and trying to preemptively require the lock under the signal
This commit is contained in:
parent
d1c668b2c1
commit
2943ffdbc2
@ -26,6 +26,21 @@ namespace Aurora::Threading::Primitives
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConditionVariableNT::AddWaiter()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto uNow = this->wlist;
|
||||||
|
auto waiting = uNow >> kShiftCountByBits;
|
||||||
|
auto uNext = ((waiting + 1) << kShiftCountByBits) | (!bool(waiting)) | (uNow & 1);
|
||||||
|
|
||||||
|
if (AuAtomicCompareExchange(&this->wlist, uNext, uNow) == uNow)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ConditionVariableNT::WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 qwTimeout, bool bSpin)
|
bool ConditionVariableNT::WaitForSignalNsEx(Win32ConditionMutex *pMutex, AuUInt64 qwTimeout, bool bSpin)
|
||||||
{
|
{
|
||||||
#if !defined(AURORA_FORCE_SRW_LOCKS)
|
#if !defined(AURORA_FORCE_SRW_LOCKS)
|
||||||
@ -39,6 +54,10 @@ namespace Aurora::Threading::Primitives
|
|||||||
auto uTargetTimeNt = AuTime::ConvertTimestampNs(uEndTimeWall);
|
auto uTargetTimeNt = AuTime::ConvertTimestampNs(uEndTimeWall);
|
||||||
bool bIOU {};
|
bool bIOU {};
|
||||||
|
|
||||||
|
this->AddWaiter();
|
||||||
|
|
||||||
|
pMutex->Unlock();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
LARGE_INTEGER word;
|
LARGE_INTEGER word;
|
||||||
@ -46,20 +65,6 @@ namespace Aurora::Threading::Primitives
|
|||||||
|
|
||||||
if (bRet)
|
if (bRet)
|
||||||
{
|
{
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
auto uNow = this->wlist;
|
|
||||||
auto waiting = uNow >> kShiftCountByBits;
|
|
||||||
auto uNext = ((waiting + 1) << kShiftCountByBits) | (!bool(waiting)) | (uNow & 1);
|
|
||||||
|
|
||||||
if (AuAtomicCompareExchange(&this->wlist, uNext, uNow) == uNow)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pMutex->Unlock();
|
|
||||||
|
|
||||||
if (gUseNativeWaitCondvar)
|
if (gUseNativeWaitCondvar)
|
||||||
{
|
{
|
||||||
// Reverted: 5b495f7fd9495aa55395666e166ac499955215dc
|
// Reverted: 5b495f7fd9495aa55395666e166ac499955215dc
|
||||||
@ -93,10 +98,8 @@ namespace Aurora::Threading::Primitives
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bRet = pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, &word) != NTSTATUS_TIMEOUT;
|
bRet = pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, &word) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pMutex->Lock();
|
|
||||||
}
|
}
|
||||||
else /* unblock NtReleaseKeyedEvent after an atomic this->wlist change <-> NtReleaseKeyedEvent race condition. */
|
else /* unblock NtReleaseKeyedEvent after an atomic this->wlist change <-> NtReleaseKeyedEvent race condition. */
|
||||||
{ /* this->wlist waiters should still be accounting for us, leading to a NtReleaseKeyedEvent block condition*/
|
{ /* this->wlist waiters should still be accounting for us, leading to a NtReleaseKeyedEvent block condition*/
|
||||||
@ -105,8 +108,6 @@ namespace Aurora::Threading::Primitives
|
|||||||
|
|
||||||
if (gUseNativeWaitCondvar)
|
if (gUseNativeWaitCondvar)
|
||||||
{
|
{
|
||||||
pMutex->Unlock();
|
|
||||||
|
|
||||||
// do not do a syscall (worst case) under ::unlock(...) barrier/pMutex's lock
|
// do not do a syscall (worst case) under ::unlock(...) barrier/pMutex's lock
|
||||||
// best case: rdtsc extrapolation, if nts hal trusts our system
|
// best case: rdtsc extrapolation, if nts hal trusts our system
|
||||||
if (uEndTimeSteady <= AuTime::SteadyClockNS())
|
if (uEndTimeSteady <= AuTime::SteadyClockNS())
|
||||||
@ -121,35 +122,31 @@ namespace Aurora::Threading::Primitives
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pMutex->Unlock();
|
|
||||||
|
|
||||||
// Obligatory Windows XP+ resched
|
// Obligatory Windows XP+ resched
|
||||||
bRet = pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, nullptr) != NTSTATUS_TIMEOUT;
|
bRet = pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, nullptr) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pMutex->Lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bRet)
|
if (!bRet)
|
||||||
{
|
{
|
||||||
auto uNow = this->wlist;
|
while (true)
|
||||||
auto uOld = (uNow >> kShiftCountByBits);
|
|
||||||
|
|
||||||
if (uOld == 0)
|
|
||||||
{
|
{
|
||||||
continue;
|
auto uNow = this->wlist;
|
||||||
}
|
auto uOld = (uNow >> kShiftCountByBits);
|
||||||
|
|
||||||
auto waiting = uOld - 1u;
|
if (uOld == 0)
|
||||||
auto uNext = waiting << kShiftCountByBits;
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (AuAtomicCompareExchange(&this->wlist, uNext, uNow) == uNow)
|
auto waiting = uOld - 1u;
|
||||||
{
|
auto uNext = waiting << kShiftCountByBits;
|
||||||
return bIOU;
|
|
||||||
}
|
if (AuAtomicCompareExchange(&this->wlist, uNext, uNow) == uNow)
|
||||||
else
|
{
|
||||||
{
|
pMutex->Lock();
|
||||||
continue;
|
return bIOU;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -157,30 +154,24 @@ namespace Aurora::Threading::Primitives
|
|||||||
if (bIOU ||
|
if (bIOU ||
|
||||||
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
|
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
|
||||||
{
|
{
|
||||||
|
pMutex->Lock();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->AddWaiter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool bIOU {};
|
bool bIOU {};
|
||||||
|
|
||||||
|
this->AddWaiter();
|
||||||
|
|
||||||
|
pMutex->Unlock();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
auto uNow = this->wlist;
|
|
||||||
auto waiting = uNow >> kShiftCountByBits;
|
|
||||||
auto uNext = ((waiting + 1) << kShiftCountByBits) | (!bool(waiting)) | (uNow & 1);
|
|
||||||
|
|
||||||
if (AuAtomicCompareExchange(&this->wlist, uNext, uNow) == uNow)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pMutex->Unlock();
|
|
||||||
|
|
||||||
if (gUseNativeWaitCondvar)
|
if (gUseNativeWaitCondvar)
|
||||||
{
|
{
|
||||||
if (bSpin &&
|
if (bSpin &&
|
||||||
@ -215,13 +206,14 @@ namespace Aurora::Threading::Primitives
|
|||||||
pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, nullptr);
|
pNtWaitForKeyedEvent(gKeyedEventHandle, (void *)&this->wlist, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pMutex->Lock();
|
|
||||||
|
|
||||||
if (bIOU ||
|
if (bIOU ||
|
||||||
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
|
(bSpin ? this->CheckOut() : this->CheckOutNoSpin()))
|
||||||
{
|
{
|
||||||
|
pMutex->Lock();
|
||||||
return bRet;
|
return bRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->AddWaiter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -21,7 +21,8 @@ namespace Aurora::Threading::Primitives
|
|||||||
void Signal();
|
void Signal();
|
||||||
void Broadcast();
|
void Broadcast();
|
||||||
void BroadcastN(AuUInt32 nBroadcast);
|
void BroadcastN(AuUInt32 nBroadcast);
|
||||||
|
|
||||||
|
auline void AddWaiter();
|
||||||
auline bool CheckOut();
|
auline bool CheckOut();
|
||||||
auline bool CheckOutNoSpin();
|
auline bool CheckOutNoSpin();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user