[*] RWLock: not implementing LockAbsMS and LockAbsNS can hurt the hotpath

This commit is contained in:
Reece Wilson 2023-08-21 15:48:42 +01:00
parent d1b1bfb221
commit 68b4fe5f8b
2 changed files with 180 additions and 15 deletions

View File

@ -33,6 +33,33 @@ namespace Aurora::Threading::Primitives
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::LockAbsMS(AuUInt64 timeout)
{
if constexpr (bIsReadView)
{
return ViewParent->LockReadNSAbs(AuMSToNS<AuUInt64>(timeout));
}
else
{
return ViewParent->LockWriteNSAbs(AuMSToNS<AuUInt64>(timeout));
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::LockAbsNS(AuUInt64 timeout)
{
if constexpr (bIsReadView)
{
return ViewParent->LockReadNSAbs(timeout);
}
else
{
return ViewParent->LockWriteNSAbs(timeout);
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::LockMS(AuUInt64 timeout)
{
@ -110,12 +137,60 @@ namespace Aurora::Threading::Primitives
return this->conditionWriter_;
#endif
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockReadNSAbs(AuUInt64 uTimeout)
{
if (this->TryLockReadNoSpin())
{
return true;
}
AuInt32 iCurState {};
do
{
iCurState = this->state_;
if (iCurState < 0)
{
AU_LOCK_GUARD(this->mutex_);
iCurState = this->state_;
if (iCurState < 0)
{
AuInt64 iSecondTimeout {};
if (uTimeout)
{
iSecondTimeout = uTimeout - AuTime::SteadyClockNS();
if (iSecondTimeout <= 0)
{
return false;
}
}
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
if (!this->GetCondition().WaitForSignalNS(iSecondTimeout))
#else
if (!this->GetCondition().WaitForSignalNsEx(AuUnsafeRaiiToShared(&this->mutex_), iSecondTimeout))
#endif
{
return false;
}
}
}
}
while (iCurState < 0 ||
AuAtomicCompareExchange(&this->state_, iCurState + 1, iCurState) != iCurState);
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockReadNS(AuUInt64 uTimeout)
{
if (this->state_ < 0 &&
this->reentrantWriteLockHandle_ == GetThreadCookie())
if (this->TryLockReadNoSpin())
{
return true;
}
@ -134,22 +209,22 @@ namespace Aurora::Threading::Primitives
iCurState = this->state_;
if (iCurState < 0)
{
AuInt64 uSecondTimeout = 0;
AuInt64 iSecondTimeout {};
if (uTimeout)
{
uSecondTimeout = uEndTime - AuTime::SteadyClockNS();
iSecondTimeout = uEndTime - AuTime::SteadyClockNS();
if (uSecondTimeout <= 0)
if (iSecondTimeout <= 0)
{
return false;
}
}
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
if (!this->GetCondition().WaitForSignalNS(uSecondTimeout))
if (!this->GetCondition().WaitForSignalNS(iSecondTimeout))
#else
if (!this->GetCondition().WaitForSignalNsEx(AuUnsafeRaiiToShared(&this->mutex_), uSecondTimeout))
if (!this->GetCondition().WaitForSignalNsEx(AuUnsafeRaiiToShared(&this->mutex_), iSecondTimeout))
#endif
{
return false;
@ -163,6 +238,85 @@ namespace Aurora::Threading::Primitives
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockWriteNSAbs(AuUInt64 uTimeout)
{
if constexpr (!bIsWriteRecursionAllowed)
{
if (TryLockWrite())
{
return true;
}
}
else
{
auto uOld = this->state_;
if (uOld < 0)
{
if (this->reentrantWriteLockHandle_ == GetThreadCookie())
{
AuAtomicSub(&this->state_, 1);
return true;
}
}
else if (uOld == 0)
{
if (AuAtomicCompareExchange(&this->state_, -1, uOld) == uOld)
{
this->reentrantWriteLockHandle_ = GetThreadCookie();
return true;
}
}
}
AuAtomicAdd(&this->writersPending_, 1);
AU_LOCK_GUARD(this->mutex_);
while (true)
{
while (this->state_ != 0)
{
AuInt64 uSecondTimeout = 0;
if (uTimeout)
{
uSecondTimeout = uTimeout - AuTime::SteadyClockNS();
if (uSecondTimeout <= 0)
{
AuAtomicSub(&this->writersPending_, 1);
return false;
}
}
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
if (!this->GetConditionWriter().WaitForSignalNS(uSecondTimeout))
#else
if (!this->GetConditionWriter().WaitForSignalNsEx(AuUnsafeRaiiToShared(&this->mutex_), uSecondTimeout))
#endif
{
AuAtomicSub(&this->writersPending_, 1);
return false;
}
}
if (AuAtomicCompareExchange(&this->state_, -1, 0) == 0)
{
this->reentrantWriteLockHandle_ = GetThreadCookie();
AuAtomicSub(&this->writersPending_, 1);
return true;
}
else
{
this->GetConditionWriter().Broadcast();
this->GetCondition().Broadcast();
}
}
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockWriteNS(AuUInt64 uTimeout)
{
@ -249,17 +403,23 @@ namespace Aurora::Threading::Primitives
{
return DoTryIf([=]()
{
auto iCurState = this->state_;
if (iCurState < 0)
{
return this->reentrantWriteLockHandle_ == GetThreadCookie();
}
return AuAtomicCompareExchange(&this->state_, iCurState + 1, iCurState) == iCurState;
return TryLockReadNoSpin();
});
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockReadNoSpin()
{
auto iCurState = this->state_;
if (iCurState < 0)
{
return this->reentrantWriteLockHandle_ == GetThreadCookie();
}
return AuAtomicCompareExchange(&this->state_, iCurState + 1, iCurState) == iCurState;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockWrite()
{

View File

@ -29,6 +29,8 @@ namespace Aurora::Threading::Primitives
bool LockMS(AuUInt64 timeout) override;
bool LockNS(AuUInt64 timeout) override;
bool LockAbsMS(AuUInt64 timeout) override;
bool LockAbsNS(AuUInt64 timeout) override;
bool TryLock() override;
@ -62,9 +64,12 @@ namespace Aurora::Threading::Primitives
~RWLockImpl();
// i dont think i'll expose a high performance interface yet
auline bool LockReadNSAbs(AuUInt64 timeout);// override;
auline bool LockReadNS(AuUInt64 timeout);// override;
auline bool LockWriteNS(AuUInt64 timeout);// override;
auline bool LockWriteNSAbs(AuUInt64 timeout);// override;
auline bool TryLockRead();// override;
auline bool TryLockReadNoSpin();// override;
auline bool TryLockWrite();// override;
auline void UnlockRead();// override;
auline void UnlockWrite();// override;