AuroraRuntime/Source/Threading/Primitives/AuRWLock.cpp
Reece d755a9d651 [*] Massive perf boost by removing atomic and
[*] Refactor ambiguous IWaitable::Lock(timeoutMs) to LockMS to prevent final using collisions
2023-04-03 08:21:44 +01:00

406 lines
10 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuRWLock.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuRWLock.hpp"
namespace Aurora::Threading::Primitives
{
template<bool bIsReadView, typename T>
void RWLockAccessView<bIsReadView, T>::Unlock()
{
if constexpr (bIsReadView)
{
this->parent_.UnlockRead();
}
else
{
this->parent_.UnlockWrite();
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::LockMS(AuUInt64 timeout)
{
if constexpr (bIsReadView)
{
return this->parent_.LockReadNS(AuMSToNS<AuUInt64>(timeout));
}
else
{
return this->parent_.LockWriteNS(AuMSToNS<AuUInt64>(timeout));
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::LockNS(AuUInt64 timeout)
{
if constexpr (bIsReadView)
{
return this->parent_.LockReadNS(timeout);
}
else
{
return this->parent_.LockWriteNS(timeout);
}
}
template<bool bIsReadView, typename T>
bool RWLockAccessView<bIsReadView, T>::TryLock()
{
if constexpr (bIsReadView)
{
return this->parent_.TryLockRead();
}
else
{
return this->parent_.TryLockWrite();
}
}
template<bool bIsWriteRecursionAllowed>
RWLockImpl<bIsWriteRecursionAllowed>::RWLockImpl() :
read_(*this),
write_(*this),
condition_(AuUnsafeRaiiToShared(&this->mutex_))
{
}
template<bool bIsWriteRecursionAllowed>
RWLockImpl<bIsWriteRecursionAllowed>::~RWLockImpl()
{
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::Init()
{
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockReadNS(AuUInt64 uTimeout)
{
if (this->state_ < 0 &&
this->reentrantWriteLockHandle_ == GetThreadCookie())
{
return true;
}
AuInt64 uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
AuInt32 iCurState {};
do
{
iCurState = this->state_;
if (iCurState < 0)
{
AU_LOCK_GUARD(this->mutex_);
iCurState = this->state_;
if (iCurState < 0)
{
AuInt64 uSecondTimeout = 0;
if (uTimeout)
{
uSecondTimeout = uEndTime - AuTime::SteadyClockNS();
if (uSecondTimeout <= 0)
{
return false;
}
}
if (!this->condition_.WaitForSignalNS(uSecondTimeout))
{
return false;
}
if (this->writersPending_)
{
this->condition_.Broadcast();
continue;
}
}
}
}
while (iCurState < 0 ||
AuAtomicCompareExchange(&this->state_, iCurState + 1, iCurState) != iCurState);
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::LockWriteNS(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;
}
}
}
AU_LOCK_GUARD(this->mutex_);
this->writersPending_++;
AuInt64 uEndTime = uTimeout ? AuTime::SteadyClockNS() + uTimeout : 0;
while (true)
{
while (this->state_ != 0)
{
AuInt64 uSecondTimeout = 0;
if (uTimeout)
{
uSecondTimeout = uEndTime - AuTime::SteadyClockNS();
if (uSecondTimeout <= 0)
{
this->writersPending_--;
return false;
}
}
if (!this->condition_.WaitForSignalNS(uSecondTimeout))
{
this->writersPending_--;
return false;
}
}
if (AuAtomicCompareExchange(&this->state_, -1, 0) == 0)
{
this->reentrantWriteLockHandle_ = GetThreadCookie();
this->writersPending_--;
return true;
}
}
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockRead()
{
auto iCurState = this->state_;
if (iCurState == -1)
{
return this->reentrantWriteLockHandle_ == GetThreadCookie();
}
return AuAtomicCompareExchange(&this->state_, iCurState + 1, iCurState) == iCurState;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::TryLockWrite()
{
for (AuUInt i = 0; i < 20; i++)
{
auto curVal = this->state_;
if (curVal < 0)
{
if constexpr (!bIsWriteRecursionAllowed)
{
AuThreading::ContextYield();
continue;
}
else
{
if (this->reentrantWriteLockHandle_ == GetThreadCookie())
{
AuAtomicSub(&this->state_, 1);
return true;
}
else
{
AuThreading::ContextYield();
continue;
}
}
}
if (curVal != 0)
{
continue;
}
if (AuAtomicCompareExchange(&this->state_, -1, curVal) == curVal)
{
this->reentrantWriteLockHandle_ = GetThreadCookie();
return true;
}
}
return false;
}
template<bool bIsWriteRecursionAllowed>
void RWLockImpl<bIsWriteRecursionAllowed>::UnlockRead()
{
if (this->state_ < 0)
{
SysAssertDbg(this->reentrantWriteLockHandle_ == GetThreadCookie());
return;
}
AU_LOCK_GUARD(this->mutex_);
auto val = AuAtomicSub(&this->state_, 1);
if ((val == 1) && (this->bElevaterPending_))
{
this->condition_.Signal();
}
if (val == 0)
{
this->condition_.Signal();
}
}
template<bool bIsWriteRecursionAllowed>
void RWLockImpl<bIsWriteRecursionAllowed>::UnlockWrite()
{
AU_LOCK_GUARD(this->mutex_);
if constexpr (!bIsWriteRecursionAllowed)
{
this->reentrantWriteLockHandle_ = 0;
this->state_ = 0;
this->condition_.Broadcast();
}
else
{
if (AuAtomicAdd(&this->state_, 1) == 0)
{
this->reentrantWriteLockHandle_ = 0;
this->condition_.Broadcast();
}
}
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::UpgradeReadToWrite(AuUInt64 timeout)
{
AU_LOCK_GUARD(this->mutex_);
while (this->state_ != 1)
{
this->bElevaterPending_ = true;
if (!this->condition_.WaitForSignal(timeout))
{
return false;
}
}
this->bElevaterPending_ = false;
this->reentrantWriteLockHandle_ = GetThreadCookie();
this->state_ = -1;
return true;
}
template<bool bIsWriteRecursionAllowed>
bool RWLockImpl<bIsWriteRecursionAllowed>::DowngradeWriteToRead()
{
AU_LOCK_GUARD(this->mutex_);
if (this->state_ != -1)
{
return false;
}
this->state_ = 1;
this->condition_.Broadcast();
return true;
}
template<bool bIsWriteRecursionAllowed>
IWaitable *RWLockImpl<bIsWriteRecursionAllowed>::AsReadable()
{
return &this->read_;
}
template<bool bIsWriteRecursionAllowed>
IWaitable *RWLockImpl<bIsWriteRecursionAllowed>::AsWritable()
{
return &this->write_;
}
AUKN_SYM IRWLock *RWLockNew()
{
auto pRwLock = _new RWLockImpl<false>();
if (!pRwLock)
{
return nullptr;
}
if (!pRwLock->Init())
{
delete pRwLock;
return nullptr;
}
return pRwLock;
}
AUKN_SYM void RWLockRelease(IRWLock *pRwLock)
{
AuSafeDelete<RWLockImpl<false> *>(pRwLock);
}
AUKN_SYM IRWLock *RWRenterableLockNew()
{
auto pRwLock = _new RWLockImpl<true>();
if (!pRwLock)
{
return nullptr;
}
if (!pRwLock->Init())
{
delete pRwLock;
return nullptr;
}
return pRwLock;
}
AUKN_SYM void RWRenterableLockRelease(IRWLock *pRwLock)
{
AuSafeDelete<RWLockImpl<true> *>(pRwLock);
}
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, RWRenterableLock, RWLockImpl<true>)
AUROXTL_INTERFACE_SOO_SRC_EX(AURORA_SYMBOL_EXPORT, RWLock, RWLockImpl<false>)
}