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:
parent
4e96239e0f
commit
615598c1b9
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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user