1
0
mirror of https://github.com/Mauler125/r5sdk.git synced 2025-02-09 19:15:03 +01:00

Tier0: implement CThreadSpinRWLock

Implementation matches that of the game executable, this will be used for CUtlStringTableMT.
This commit is contained in:
Kawe Mazidjatari 2024-03-14 02:33:57 +01:00
parent 4e96239e0f
commit 615598c1b9
2 changed files with 321 additions and 5 deletions
r5dev

@ -95,11 +95,6 @@ FORCEINLINE bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value,
// Thread checking methods.
//
//-----------------------------------------------------------------------------
#ifndef BUILDING_MATHLIB
extern ThreadId_t* g_ThreadMainThreadID;
extern ThreadId_t* g_ThreadServerFrameThreadID;
FORCEINLINE ThreadId_t ThreadGetCurrentId()
{
#ifdef _WIN32
@ -117,6 +112,11 @@ FORCEINLINE ThreadId_t ThreadGetCurrentId()
#endif
}
#ifndef BUILDING_MATHLIB
extern ThreadId_t* g_ThreadMainThreadID;
extern ThreadId_t* g_ThreadServerFrameThreadID;
FORCEINLINE bool ThreadInMainThread()
{
return (ThreadGetCurrentId() == (*g_ThreadMainThreadID));
@ -340,6 +340,221 @@ template <> struct CAutoLockTypeDeducer<sizeof(CThreadFastMutex)> { typedef CThr
AUTO_LOCK_(CAutoLockTypeDeducer<sizeof(mutex)>::Type_t, mutex)
#endif
//-----------------------------------------------------------------------------
//
// CThreadSpinRWLock inline functions
//
//-----------------------------------------------------------------------------
class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock
{
public:
CThreadSpinRWLock()
{
m_lockInfo.m_i32 = 0;
m_writerId = 0;
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
m_iWriteDepth = 0;
#endif
}
bool IsLockedForWrite();
bool IsLockedForRead();
FORCEINLINE bool TryLockForWrite();
bool TryLockForWrite_UnforcedInline();
void LockForWrite();
void SpinLockForWrite();
FORCEINLINE bool TryLockForRead();
bool TryLockForRead_UnforcedInline();
void LockForRead();
void SpinLockForRead();
void UnlockWrite();
void UnlockRead();
bool TryLockForWrite() const { return const_cast<CThreadSpinRWLock*>(this)->TryLockForWrite(); }
bool TryLockForRead() const { return const_cast<CThreadSpinRWLock*>(this)->TryLockForRead(); }
void LockForRead() const { const_cast<CThreadSpinRWLock*>(this)->LockForRead(); }
void UnlockRead() const { const_cast<CThreadSpinRWLock*>(this)->UnlockRead(); }
void LockForWrite() const { const_cast<CThreadSpinRWLock*>(this)->LockForWrite(); }
void UnlockWrite() const { const_cast<CThreadSpinRWLock*>(this)->UnlockWrite(); }
private:
enum
{
THREAD_SPIN = (8 * 1024)
};
#pragma warning(push)
#pragma warning(disable : 4201)
union LockInfo_t
{
struct
{
#if PLAT_LITTLE_ENDIAN
uint16 m_nReaders;
uint16 m_fWriting;
#else
uint16 m_fWriting;
uint16 m_nReaders;
#endif
};
uint32 m_i32;
};
#pragma warning(pop)
LockInfo_t m_lockInfo;
ThreadId_t m_writerId;
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
int m_iWriteDepth;
uint32 pad;
#endif
} ALIGN8_POST;
inline bool CThreadSpinRWLock::IsLockedForWrite()
{
return (m_lockInfo.m_fWriting == 1);
}
inline bool CThreadSpinRWLock::IsLockedForRead()
{
return (m_lockInfo.m_nReaders > 0);
}
FORCEINLINE bool CThreadSpinRWLock::TryLockForWrite()
{
volatile LockInfo_t& curValue = m_lockInfo;
if (!(curValue.m_i32 & 0x00010000) && ThreadInterlockedAssignIf((LONG*)&curValue.m_i32, 0x00010000, 0))
{
ThreadMemoryBarrier();
Assert(m_iWriteDepth == 0 && m_writerId == 0);
m_writerId = ThreadGetCurrentId();
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
m_iWriteDepth++;
#endif
return true;
}
return false;
}
inline bool CThreadSpinRWLock::TryLockForWrite_UnforcedInline()
{
if (TryLockForWrite())
{
return true;
}
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
if (m_writerId != ThreadGetCurrentId())
{
return false;
}
m_iWriteDepth++;
return true;
#else
return false;
#endif
}
FORCEINLINE void CThreadSpinRWLock::LockForWrite()
{
if (!TryLockForWrite())
{
SpinLockForWrite();
}
}
FORCEINLINE bool CThreadSpinRWLock::TryLockForRead()
{
volatile LockInfo_t& curValue = m_lockInfo;
if (!(curValue.m_i32 & 0x00010000)) // !m_lockInfo.m_fWriting
{
LockInfo_t oldValue;
LockInfo_t newValue;
oldValue.m_i32 = (curValue.m_i32 & 0xffff);
newValue.m_i32 = oldValue.m_i32 + 1;
if (ThreadInterlockedAssignIf((LONG*)&m_lockInfo.m_i32, newValue.m_i32, oldValue.m_i32))
{
ThreadMemoryBarrier();
Assert(m_lockInfo.m_fWriting == 0);
return true;
}
}
return false;
}
inline bool CThreadSpinRWLock::TryLockForRead_UnforcedInline()
{
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
if (m_lockInfo.m_i32 & 0x00010000) // m_lockInfo.m_fWriting
{
if (m_writerId == ThreadGetCurrentId())
{
m_lockInfo.m_nReaders++;
return true;
}
return false;
}
#endif
return TryLockForRead();
}
FORCEINLINE void CThreadSpinRWLock::LockForRead()
{
if (!TryLockForRead())
{
SpinLockForRead();
}
}
FORCEINLINE void CThreadSpinRWLock::UnlockWrite()
{
Assert(m_writerId == ThreadGetCurrentId());
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
if (--m_iWriteDepth == 0)
#endif
{
m_writerId = 0;
ThreadMemoryBarrier();
m_lockInfo.m_i32 = 0;
}
}
#ifndef REENTRANT_THREAD_SPIN_RW_LOCK
FORCEINLINE
#else
inline
#endif
void CThreadSpinRWLock::UnlockRead()
{
Assert(m_writerId == 0 || (m_writerId == ThreadGetCurrentId() && m_lockInfo.m_fWriting));
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
if (!(m_lockInfo.m_i32 & 0x00010000)) // !m_lockInfo.m_fWriting
#endif
{
ThreadMemoryBarrier();
ThreadInterlockedDecrement((int32*)&m_lockInfo.m_i32);
Assert(m_writerId == 0 && !m_lockInfo.m_fWriting);
}
#ifdef REENTRANT_THREAD_SPIN_RW_LOCK
else if (m_writerId == ThreadGetCurrentId())
{
m_lockInfo.m_nReaders--;
}
else
{
RWLAssert(0);
}
#endif
}
#ifndef BUILDING_MATHLIB
///////////////////////////////////////////////////////////////////////////////
class VThreadTools : public IDetour

@ -97,6 +97,107 @@ int CThreadFastMutex::Unlock()
return result;
}
void CThreadSpinRWLock::SpinLockForWrite()
{
int i;
if (TryLockForWrite_UnforcedInline())
{
return;
}
for (i = THREAD_SPIN; i != 0; --i)
{
if (TryLockForWrite_UnforcedInline())
{
return;
}
ThreadPause();
}
for (i = THREAD_SPIN; i != 0; --i)
{
if (TryLockForWrite_UnforcedInline())
{
return;
}
ThreadPause();
if (i % 1024 == 0)
{
ThreadSleep(0);
}
}
for (i = THREAD_SPIN * 4; i != 0; --i)
{
if (TryLockForWrite_UnforcedInline())
{
return;
}
ThreadPause();
ThreadSleep(0);
}
for (;; ) // coded as for instead of while to make easy to breakpoint success
{
if (TryLockForWrite_UnforcedInline())
{
return;
}
ThreadPause();
ThreadSleep(1);
}
}
void CThreadSpinRWLock::SpinLockForRead()
{
int i;
for (i = THREAD_SPIN; i != 0; --i)
{
if (TryLockForRead_UnforcedInline())
{
return;
}
ThreadPause();
}
for (i = THREAD_SPIN; i != 0; --i)
{
if (TryLockForRead_UnforcedInline())
{
return;
}
ThreadPause();
if (i % 1024 == 0)
{
ThreadSleep(0);
}
}
for (i = THREAD_SPIN * 4; i != 0; --i)
{
if (TryLockForRead_UnforcedInline())
{
return;
}
ThreadPause();
ThreadSleep(0);
}
for (;; ) // coded as for instead of while to make easy to breakpoint success
{
if (TryLockForRead_UnforcedInline())
{
return;
}
ThreadPause();
ThreadSleep(1);
}
}
// NOTE: originally the game exported 'ThreadInMainThread()' and ThreadInServerFrameThread(),
// but since the game is built static, and all instances of said functions are inline, we had
// to export the variable symbols instead and get them here to reimplement said functions.