[*] RWLock: WakeOnAddress optimization on wait to prevent mutex congestion on modern OSes
This commit is contained in:
parent
30fc75cbab
commit
d79cb4f3ca
@ -371,6 +371,7 @@ namespace Aurora
|
|||||||
AuUInt64 uAdaptiveSpinCUCnt4 : 4 { 2 };
|
AuUInt64 uAdaptiveSpinCUCnt4 : 4 { 2 };
|
||||||
AuUInt64 uAdaptiveSpinCUCnt8 : 4 { 3 };
|
AuUInt64 uAdaptiveSpinCUCnt8 : 4 { 3 };
|
||||||
AuUInt64 uAdaptiveSpinCUCnt16 : 4 { 4 };
|
AuUInt64 uAdaptiveSpinCUCnt16 : 4 { 4 };
|
||||||
|
AuUInt64 bPreferFutexRWLock : 1 { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DummyConfig
|
struct DummyConfig
|
||||||
|
@ -29,7 +29,7 @@ namespace Aurora::Threading::Primitives
|
|||||||
* @brief Allows for read-to-write upgrades when the decision to esclate is made based upon a shared resource
|
* @brief Allows for read-to-write upgrades when the decision to esclate is made based upon a shared resource
|
||||||
* which would be lost by unlocking and relocking in exclusive mode.
|
* which would be lost by unlocking and relocking in exclusive mode.
|
||||||
* You must be careful to consider how this is used to prevent dead-locks
|
* You must be careful to consider how this is used to prevent dead-locks
|
||||||
* @param timeout
|
* @param timeout in relative nanoseconds
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual bool UpgradeReadToWrite(AuUInt64 timeout) = 0;
|
virtual bool UpgradeReadToWrite(AuUInt64 timeout) = 0;
|
||||||
|
@ -18,6 +18,12 @@ namespace Aurora::Threading::Primitives
|
|||||||
#define ViewParent ((T *)(((char *)this) - (bIsReadView ? RWLockImpl<true>::kOffsetOfRead : RWLockImpl<true>::kOffsetOfWrite)))
|
#define ViewParent ((T *)(((char *)this) - (bIsReadView ? RWLockImpl<true>::kOffsetOfRead : RWLockImpl<true>::kOffsetOfWrite)))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(AURORA_COMPILER_MSVC)
|
||||||
|
#define RWLOCK_REORDER_BARRIER() ::MemoryBarrier();
|
||||||
|
#else
|
||||||
|
#define RWLOCK_REORDER_BARRIER()
|
||||||
|
#endif
|
||||||
|
|
||||||
static const auto kRWThreadWriterHardContextSwitchBias = 15;
|
static const auto kRWThreadWriterHardContextSwitchBias = 15;
|
||||||
|
|
||||||
template<bool bIsReadView, typename T>
|
template<bool bIsReadView, typename T>
|
||||||
@ -137,6 +143,18 @@ namespace Aurora::Threading::Primitives
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
AuUInt32 *RWLockImpl<bIsWriteRecursionAllowed>::GetFutexCondition()
|
||||||
|
{
|
||||||
|
return (AuUInt32 *)&this->state_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
AuUInt32 *RWLockImpl<bIsWriteRecursionAllowed>::GetFutexConditionWriter()
|
||||||
|
{
|
||||||
|
return (AuUInt32 *)this->conditionVariableWriter_;
|
||||||
|
}
|
||||||
|
|
||||||
template<bool bIsWriteRecursionAllowed>
|
template<bool bIsWriteRecursionAllowed>
|
||||||
bool RWLockImpl<bIsWriteRecursionAllowed>::LockReadNSAbs(AuUInt64 uTimeout)
|
bool RWLockImpl<bIsWriteRecursionAllowed>::LockReadNSAbs(AuUInt64 uTimeout)
|
||||||
{
|
{
|
||||||
@ -152,31 +170,41 @@ namespace Aurora::Threading::Primitives
|
|||||||
|
|
||||||
if (iCurState < 0)
|
if (iCurState < 0)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (gUseFutexRWLock)
|
||||||
|
|
||||||
iCurState = this->state_;
|
|
||||||
if (iCurState < 0)
|
|
||||||
{
|
{
|
||||||
AuInt64 iSecondTimeout {};
|
if (!WaitOnAddressSteady((const void *)&this->state_, &iCurState, sizeof(iCurState), uTimeout))
|
||||||
|
|
||||||
if (uTimeout)
|
|
||||||
{
|
{
|
||||||
iSecondTimeout = AuInt64(uTimeout) - AuTime::SteadyClockNS();
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
|
||||||
if (iSecondTimeout <= 0)
|
iCurState = this->state_;
|
||||||
|
if (iCurState < 0)
|
||||||
|
{
|
||||||
|
AuInt64 iSecondTimeout {};
|
||||||
|
|
||||||
|
if (uTimeout)
|
||||||
|
{
|
||||||
|
iSecondTimeout = AuInt64(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(&this->mutex_, iSecondTimeout))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
|
||||||
if (!this->GetCondition().WaitForSignalNS(iSecondTimeout))
|
|
||||||
#else
|
|
||||||
if (!this->GetCondition().WaitForSignalNsEx(&this->mutex_, iSecondTimeout))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,31 +236,41 @@ namespace Aurora::Threading::Primitives
|
|||||||
|
|
||||||
if (iCurState < 0)
|
if (iCurState < 0)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (gUseFutexRWLock)
|
||||||
|
|
||||||
iCurState = this->state_;
|
|
||||||
if (iCurState < 0)
|
|
||||||
{
|
{
|
||||||
AuInt64 iSecondTimeout {};
|
if (!WaitOnAddressSteady((const void *)&this->state_, &iCurState, sizeof(iCurState), uEndTime))
|
||||||
|
|
||||||
if (uTimeout)
|
|
||||||
{
|
{
|
||||||
iSecondTimeout = uEndTime - AuTime::SteadyClockNS();
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
|
||||||
if (iSecondTimeout <= 0)
|
iCurState = this->state_;
|
||||||
|
if (iCurState < 0)
|
||||||
|
{
|
||||||
|
AuInt64 iSecondTimeout {};
|
||||||
|
|
||||||
|
if (uTimeout)
|
||||||
|
{
|
||||||
|
iSecondTimeout = uEndTime - AuTime::SteadyClockNS();
|
||||||
|
|
||||||
|
if (iSecondTimeout <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
||||||
|
if (!this->GetCondition().WaitForSignalNS(iSecondTimeout))
|
||||||
|
#else
|
||||||
|
if (!this->GetCondition().WaitForSignalNsEx(&this->mutex_, iSecondTimeout))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
|
||||||
if (!this->GetCondition().WaitForSignalNS(iSecondTimeout))
|
|
||||||
#else
|
|
||||||
if (!this->GetCondition().WaitForSignalNsEx(&this->mutex_, iSecondTimeout))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,75 +292,58 @@ namespace Aurora::Threading::Primitives
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto uOld = this->state_;
|
if (gUseFutexRWLock)
|
||||||
if (uOld < 0)
|
|
||||||
{
|
{
|
||||||
if (this->reentrantWriteLockHandle_ == GetThreadCookie())
|
if (DoTryIf([=]()
|
||||||
|
{
|
||||||
|
return this->LockWriteNSAbsSecondPath();
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
AuAtomicSub(&this->state_, 1);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (uOld == 0)
|
else
|
||||||
{
|
{
|
||||||
if (AuAtomicCompareExchange(&this->state_, -1, uOld) == uOld)
|
if (this->LockWriteNSAbsSecondPath())
|
||||||
{
|
{
|
||||||
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (gUseFutexRWLock)
|
||||||
AuAtomicAdd(&this->writersPending_, 1);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
while (this->state_ != 0)
|
return this->LockWriteNSAbsUnlocked(uTimeout);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
return this->LockWriteNSAbsUnlocked(uTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
bool RWLockImpl<bIsWriteRecursionAllowed>::LockWriteNSAbsSecondPath()
|
||||||
|
{
|
||||||
|
auto uOld = this->state_;
|
||||||
|
if (uOld < 0)
|
||||||
|
{
|
||||||
|
if (this->reentrantWriteLockHandle_ == GetThreadCookie())
|
||||||
{
|
{
|
||||||
AuInt64 iSecondTimeout = 0;
|
AuAtomicSub(&this->state_, 1);
|
||||||
|
return true;
|
||||||
if (uTimeout)
|
|
||||||
{
|
|
||||||
iSecondTimeout = AuInt64(uTimeout) - AuTime::SteadyClockNS();
|
|
||||||
|
|
||||||
if (iSecondTimeout <= 0)
|
|
||||||
{
|
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
|
||||||
bool bStatus = this->GetConditionWriter().WaitForSignalNS(iSecondTimeout);
|
|
||||||
#else
|
|
||||||
bool bStatus = this->GetConditionWriter().WaitForSignalNsEx(&this->mutex_, iSecondTimeout);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if constexpr (bIsWriteRecursionAllowed)
|
|
||||||
{
|
|
||||||
if (this->state_ == 1)
|
|
||||||
{
|
|
||||||
this->GetConditionWriter().Broadcast();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bStatus)
|
|
||||||
{
|
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (AuAtomicCompareExchange(&this->state_, -1, 0) == 0)
|
else if (uOld == 0)
|
||||||
|
{
|
||||||
|
if (AuAtomicCompareExchange(&this->state_, -1, uOld) == uOld)
|
||||||
{
|
{
|
||||||
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool bIsWriteRecursionAllowed>
|
template<bool bIsWriteRecursionAllowed>
|
||||||
@ -356,45 +377,110 @@ namespace Aurora::Threading::Primitives
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (gUseFutexRWLock)
|
||||||
AuAtomicAdd(&this->writersPending_, 1);
|
{
|
||||||
|
AuInt64 uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
|
||||||
|
|
||||||
AuInt64 uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
|
return this->LockWriteNSAbsUnlocked(uEndTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
|
||||||
|
AuInt64 uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
|
||||||
|
|
||||||
|
return this->LockWriteNSAbsUnlocked(uEndTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
bool RWLockImpl<bIsWriteRecursionAllowed>::LockWriteNSAbsUnlocked(AuUInt64 qwTimeoutNS)
|
||||||
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
while (this->state_ != 0)
|
AuInt32 iCurState;
|
||||||
|
while ((iCurState = this->state_) != 0)
|
||||||
{
|
{
|
||||||
AuInt64 uSecondTimeout = 0;
|
AuInt64 uSecondTimeout = 0;
|
||||||
|
|
||||||
if (uTimeout)
|
bool bStatus {};
|
||||||
|
if (gUseFutexRWLock)
|
||||||
{
|
{
|
||||||
uSecondTimeout = uEndTime - AuTime::SteadyClockNS();
|
auto pSemaphore = this->GetFutexConditionWriter();
|
||||||
|
|
||||||
if (uSecondTimeout <= 0)
|
AuInt32 iCurState;
|
||||||
|
while ((iCurState = this->state_) != 0)
|
||||||
{
|
{
|
||||||
|
bool bStatusTwo {};
|
||||||
|
AuAtomicAdd(&this->writersPending_, 1);
|
||||||
|
static const AuUInt32 kExpect { 0 };
|
||||||
|
RWLOCK_REORDER_BARRIER();
|
||||||
|
if ((iCurState = this->state_) == 0)
|
||||||
|
{
|
||||||
|
bStatus = true;
|
||||||
|
bStatusTwo = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bStatus = WaitOnAddress(pSemaphore, &kExpect, sizeof(kExpect), qwTimeoutNS);
|
||||||
|
}
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
return false;
|
|
||||||
|
if (!bStatus)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bStatusTwo)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto uState = *pSemaphore;
|
||||||
|
|
||||||
|
if (uState == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuAtomicCompareExchange(pSemaphore, uState - 1, uState) == uState)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (qwTimeoutNS)
|
||||||
|
{
|
||||||
|
uSecondTimeout = qwTimeoutNS - AuTime::SteadyClockNS();
|
||||||
|
|
||||||
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
if (uSecondTimeout <= 0)
|
||||||
bool bStatus = this->GetConditionWriter().WaitForSignalNS(uSecondTimeout);
|
{
|
||||||
#else
|
return false;
|
||||||
bool bStatus = this->GetConditionWriter().WaitForSignalNsEx(&this->mutex_, uSecondTimeout);
|
}
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
AuAtomicAdd(&this->writersPending_, 1);
|
||||||
|
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
||||||
|
bStatus = this->GetConditionWriter().WaitForSignalNS(uSecondTimeout);
|
||||||
|
#else
|
||||||
|
bStatus = this->GetConditionWriter().WaitForSignalNsEx(&this->mutex_, uSecondTimeout);
|
||||||
|
#endif
|
||||||
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if constexpr (bIsWriteRecursionAllowed)
|
if constexpr (bIsWriteRecursionAllowed)
|
||||||
{
|
{
|
||||||
if (this->state_ == 1)
|
if (this->state_ == 1)
|
||||||
{
|
{
|
||||||
this->GetConditionWriter().Broadcast();
|
this->SignalManyWriter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bStatus)
|
if (!bStatus)
|
||||||
{
|
{
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,7 +488,6 @@ namespace Aurora::Threading::Primitives
|
|||||||
if (AuAtomicCompareExchange(&this->state_, -1, 0) == 0)
|
if (AuAtomicCompareExchange(&this->state_, -1, 0) == 0)
|
||||||
{
|
{
|
||||||
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,6 +495,63 @@ namespace Aurora::Threading::Primitives
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
void RWLockImpl<bIsWriteRecursionAllowed>::SignalOneReader()
|
||||||
|
{
|
||||||
|
if (gUseFutexRWLock)
|
||||||
|
{
|
||||||
|
WakeOnAddress((const void *)&this->state_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->GetCondition().Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
void RWLockImpl<bIsWriteRecursionAllowed>::SignalOneWriter()
|
||||||
|
{
|
||||||
|
if (gUseFutexRWLock)
|
||||||
|
{
|
||||||
|
auto pThat = this->GetFutexConditionWriter();
|
||||||
|
AuAtomicAdd(pThat, 1u);
|
||||||
|
WakeOnAddress(pThat);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->GetConditionWriter().Signal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
void RWLockImpl<bIsWriteRecursionAllowed>::SignalManyReader()
|
||||||
|
{
|
||||||
|
if (gUseFutexRWLock)
|
||||||
|
{
|
||||||
|
WakeAllOnAddress((const void *)&this->state_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->GetCondition().Broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
void RWLockImpl<bIsWriteRecursionAllowed>::SignalManyWriter()
|
||||||
|
{
|
||||||
|
if (gUseFutexRWLock)
|
||||||
|
{
|
||||||
|
auto pThat = this->GetFutexConditionWriter();
|
||||||
|
AuUInt32 uCount = this->writersPending_;
|
||||||
|
AuAtomicAdd(pThat, uCount);
|
||||||
|
WakeNOnAddress(pThat, uCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->GetConditionWriter().Broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<bool bIsWriteRecursionAllowed>
|
template<bool bIsWriteRecursionAllowed>
|
||||||
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockRead()
|
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockRead()
|
||||||
{
|
{
|
||||||
@ -506,18 +648,24 @@ namespace Aurora::Threading::Primitives
|
|||||||
{
|
{
|
||||||
bool bElevation {};
|
bool bElevation {};
|
||||||
|
|
||||||
|
if (!gUseFutexRWLock)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
AU_LOCK_GUARD(this->mutex_); /* actually locking this->state_, out of branch. required for the mutually exclusive correctness of the condition. this is a fence. */
|
||||||
|
bElevation = this->writersPending_ > 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* atomic read */
|
||||||
bElevation = this->writersPending_ > 0;
|
bElevation = this->writersPending_ > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bElevation)
|
if (bElevation)
|
||||||
{
|
{
|
||||||
this->GetConditionWriter().Signal();
|
this->SignalOneWriter();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->GetCondition().Broadcast();
|
this->SignalManyReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,19 +679,33 @@ namespace Aurora::Threading::Primitives
|
|||||||
{
|
{
|
||||||
this->reentrantWriteLockHandle_ = 0;
|
this->reentrantWriteLockHandle_ = 0;
|
||||||
|
|
||||||
|
if (!gUseFutexRWLock)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
#if defined(AURORA_COMPILER_MSVC)
|
||||||
this->state_ = 0;
|
this->state_ = 0;
|
||||||
|
#else
|
||||||
|
__sync_lock_release(&this->state_);
|
||||||
|
#endif
|
||||||
bElevationPending = this->writersPending_ > 0;
|
bElevationPending = this->writersPending_ > 0;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bElevationPending = this->writersPending_ > 0;
|
||||||
|
#if defined(AURORA_COMPILER_MSVC)
|
||||||
|
this->state_ = 0;
|
||||||
|
#else
|
||||||
|
__sync_lock_release(&this->state_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (bElevationPending)
|
if (bElevationPending)
|
||||||
{
|
{
|
||||||
this->GetConditionWriter().Signal();
|
this->SignalOneWriter();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->GetCondition().Broadcast();
|
this->SignalManyReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -568,18 +730,23 @@ namespace Aurora::Threading::Primitives
|
|||||||
|
|
||||||
if (val == 0)
|
if (val == 0)
|
||||||
{
|
{
|
||||||
|
if (!gUseFutexRWLock)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
bElevationPending = this->writersPending_ > 0;
|
bElevationPending = this->writersPending_ > 0;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bElevationPending = this->writersPending_ > 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (bElevationPending)
|
if (bElevationPending)
|
||||||
{
|
{
|
||||||
this->GetConditionWriter().Signal();
|
this->SignalOneWriter();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->GetCondition().Broadcast();
|
this->SignalManyReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -590,65 +757,158 @@ namespace Aurora::Threading::Primitives
|
|||||||
{
|
{
|
||||||
if (this->state_ == 1)
|
if (this->state_ == 1)
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (gUseFutexRWLock)
|
||||||
|
|
||||||
if (this->state_ == 1)
|
|
||||||
{
|
{
|
||||||
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
if (this->UpgradeReadToWriteDoUpgrade())
|
||||||
this->state_ = -1;
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
|
||||||
|
if (this->UpgradeReadToWriteDoUpgrade())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
|
auto uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
|
||||||
|
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
if (!gUseFutexRWLock)
|
||||||
AuAtomicAdd(&this->writersPending_, 1);
|
|
||||||
|
|
||||||
while (this->state_ != 1)
|
|
||||||
{
|
{
|
||||||
AuInt64 iSecondTimeout {};
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
AuAtomicAdd(&this->writersPending_, 1);
|
||||||
|
|
||||||
if (uTimeout)
|
while (this->state_ != 1)
|
||||||
{
|
{
|
||||||
iSecondTimeout = AuInt64(uEndTime) - AuTime::SteadyClockNS();
|
AuInt64 iSecondTimeout {};
|
||||||
|
|
||||||
if (iSecondTimeout <= 0)
|
if (uTimeout)
|
||||||
|
{
|
||||||
|
iSecondTimeout = AuInt64(uEndTime) - AuTime::SteadyClockNS();
|
||||||
|
|
||||||
|
if (iSecondTimeout <= 0)
|
||||||
|
{
|
||||||
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
||||||
|
if (!this->GetConditionWriter().WaitForSignalNS(iSecondTimeout))
|
||||||
|
#else
|
||||||
|
if (!this->GetConditionWriter().WaitForSignalNsEx(&this->mutex_, iSecondTimeout))
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR)
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
if (!this->GetConditionWriter().WaitForSignalNS(iSecondTimeout))
|
return this->UpgradeReadToWriteDoUpgrade();
|
||||||
#else
|
}
|
||||||
if (!this->GetConditionWriter().WaitForSignalNsEx(&this->mutex_, iSecondTimeout))
|
else
|
||||||
#endif
|
{
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
return false;
|
auto pSemaphore = this->GetFutexConditionWriter();
|
||||||
|
|
||||||
|
AuInt32 iCurState;
|
||||||
|
while ((iCurState = this->state_) != 1)
|
||||||
|
{
|
||||||
|
bool bStatusTwo {};
|
||||||
|
bool bStatus {};
|
||||||
|
|
||||||
|
AuAtomicAdd(&this->writersPending_, 1);
|
||||||
|
static const AuUInt32 kExpect { 0 };
|
||||||
|
RWLOCK_REORDER_BARRIER();
|
||||||
|
if ((iCurState = this->state_) == 1)
|
||||||
|
{
|
||||||
|
bStatus = true;
|
||||||
|
bStatusTwo = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bStatus = WaitOnAddress(pSemaphore, &kExpect, sizeof(kExpect), uEndTime);
|
||||||
|
}
|
||||||
|
AuAtomicSub(&this->writersPending_, 1);
|
||||||
|
|
||||||
|
if (!bStatus)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bStatusTwo)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto uState = *pSemaphore;
|
||||||
|
|
||||||
|
if (uState == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuAtomicCompareExchange(pSemaphore, uState - 1, uState) == uState)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->UpgradeReadToWriteDoUpgrade())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AuAtomicSub(&this->writersPending_, 1);
|
/* unreachable */
|
||||||
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
return false;
|
||||||
this->state_ = -1;
|
}
|
||||||
return true;
|
|
||||||
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
bool RWLockImpl<bIsWriteRecursionAllowed>::UpgradeReadToWriteDoUpgrade()
|
||||||
|
{
|
||||||
|
if (AuAtomicCompareExchange(&this->state_, -1, 1) == 1)
|
||||||
|
{
|
||||||
|
this->reentrantWriteLockHandle_ = GetThreadCookie();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool bIsWriteRecursionAllowed>
|
template<bool bIsWriteRecursionAllowed>
|
||||||
bool RWLockImpl<bIsWriteRecursionAllowed>::DowngradeWriteToRead()
|
bool RWLockImpl<bIsWriteRecursionAllowed>::DowngradeWriteToRead()
|
||||||
{
|
{
|
||||||
|
if (gUseFutexRWLock)
|
||||||
|
{
|
||||||
|
if (AuAtomicCompareExchange(&this->state_, 1, -1) == -1)
|
||||||
|
{
|
||||||
|
this->SignalManyReader();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AU_LOCK_GUARD(this->mutex_);
|
AU_LOCK_GUARD(this->mutex_);
|
||||||
|
|
||||||
if (this->state_ != -1)
|
if (AuAtomicCompareExchange(&this->state_, 1, -1) == -1)
|
||||||
|
{
|
||||||
|
this->SignalManyReader();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state_ = 1;
|
|
||||||
this->GetCondition().Broadcast();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool bIsWriteRecursionAllowed>
|
template<bool bIsWriteRecursionAllowed>
|
||||||
|
@ -74,8 +74,13 @@ namespace Aurora::Threading::Primitives
|
|||||||
auline void UnlockRead();// override;
|
auline void UnlockRead();// override;
|
||||||
auline void UnlockWrite();// override;
|
auline void UnlockWrite();// override;
|
||||||
|
|
||||||
|
|
||||||
|
auline bool LockWriteNSAbsSecondPath();// override;
|
||||||
|
auline bool LockWriteNSAbsUnlocked(AuUInt64 qwTimeoutNS);// override;
|
||||||
|
|
||||||
bool UpgradeReadToWrite(AuUInt64 timeout) override;
|
bool UpgradeReadToWrite(AuUInt64 timeout) override;
|
||||||
bool DowngradeWriteToRead() override;
|
bool DowngradeWriteToRead() override;
|
||||||
|
auline bool UpgradeReadToWriteDoUpgrade();
|
||||||
|
|
||||||
IWaitable *AsReadable() override;
|
IWaitable *AsReadable() override;
|
||||||
IWaitable *AsWritable() override;
|
IWaitable *AsWritable() override;
|
||||||
@ -83,6 +88,14 @@ namespace Aurora::Threading::Primitives
|
|||||||
auline ConditionVariableInternal &GetCondition();
|
auline ConditionVariableInternal &GetCondition();
|
||||||
auline ConditionVariableInternal &GetConditionWriter();
|
auline ConditionVariableInternal &GetConditionWriter();
|
||||||
|
|
||||||
|
auline AuUInt32 *GetFutexCondition();
|
||||||
|
auline AuUInt32 *GetFutexConditionWriter();
|
||||||
|
|
||||||
|
auline void SignalOneReader();
|
||||||
|
auline void SignalOneWriter();
|
||||||
|
auline void SignalManyReader();
|
||||||
|
auline void SignalManyWriter();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
RWLockAccessView<true, RWLockImpl> read_;
|
RWLockAccessView<true, RWLockImpl> read_;
|
||||||
|
@ -53,6 +53,8 @@ namespace Aurora::Threading
|
|||||||
|
|
||||||
Primitives::InitAdaptiveThreshold();
|
Primitives::InitAdaptiveThreshold();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AUKN_SYM bool IsWaitOnRecommended();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Aurora::Threading::Primitives
|
namespace Aurora::Threading::Primitives
|
||||||
@ -62,6 +64,8 @@ namespace Aurora::Threading::Primitives
|
|||||||
auto uCores = AuHwInfo::GetCPUInfo().uThreads;
|
auto uCores = AuHwInfo::GetCPUInfo().uThreads;
|
||||||
|
|
||||||
gSpinLinearPart = gRuntimeConfig.threadingConfig.uSpinLoopLinearBit;
|
gSpinLinearPart = gRuntimeConfig.threadingConfig.uSpinLoopLinearBit;
|
||||||
|
gUseFutexRWLock = gRuntimeConfig.threadingConfig.bPreferFutexRWLock &&
|
||||||
|
IsWaitOnRecommended();
|
||||||
|
|
||||||
if (!gRuntimeConfig.threadingConfig.bForceEnableAdaptiveSpin)
|
if (!gRuntimeConfig.threadingConfig.bForceEnableAdaptiveSpin)
|
||||||
{
|
{
|
||||||
@ -69,7 +73,11 @@ namespace Aurora::Threading::Primitives
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uCores >= 16)
|
if (uCores == 1)
|
||||||
|
{
|
||||||
|
gSpinAdaptiveThreshold = 0;
|
||||||
|
}
|
||||||
|
else if (uCores >= 16)
|
||||||
{
|
{
|
||||||
gSpinAdaptiveThreshold = uCores / gRuntimeConfig.threadingConfig.uAdaptiveSpinCUCnt16;
|
gSpinAdaptiveThreshold = uCores / gRuntimeConfig.threadingConfig.uAdaptiveSpinCUCnt16;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ namespace Aurora::Threading::Primitives
|
|||||||
inline AuUInt32 gSpinAdaptiveCurrentCount {};
|
inline AuUInt32 gSpinAdaptiveCurrentCount {};
|
||||||
inline AuUInt32 gSpinLinearPart {};
|
inline AuUInt32 gSpinLinearPart {};
|
||||||
|
|
||||||
|
inline AuUInt32 gUseFutexRWLock {};
|
||||||
|
|
||||||
void InitAdaptiveThreshold();
|
void InitAdaptiveThreshold();
|
||||||
void InitAdaptiveThresholdFirstTime();
|
void InitAdaptiveThresholdFirstTime();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user