From 9b74ac2e53de21f21f28d46d341a04dff6b4eb4f Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sun, 2 Jul 2023 11:38:36 +0200 Subject: [PATCH] Improve threadtools * Added and utilized auto lock/unlock mechanism. * Rebuild CThreadFastMutex's 'Lock' and 'Unlock' methods in the SDK. * Forced thread intrinsics and ID checkers as inline. --- r5dev/datacache/mdlcache.cpp | 18 ++-- r5dev/public/tier0/basetypes.h | 15 +++- r5dev/public/tier0/threadtools.h | 149 ++++++++++++++++++++++++------- r5dev/tier0/threadtools.cpp | 129 +++++++++++++++++--------- 4 files changed, 222 insertions(+), 89 deletions(-) diff --git a/r5dev/datacache/mdlcache.cpp b/r5dev/datacache/mdlcache.cpp index ff67a43b..ede87af7 100644 --- a/r5dev/datacache/mdlcache.cpp +++ b/r5dev/datacache/mdlcache.cpp @@ -28,8 +28,8 @@ std::unordered_set g_vBadMDLHandles; //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::FindMDL(CMDLCache* cache, MDLHandle_t handle, void* a3) { - studiohdr_t* pStudioHdr; // rax studiodata_t* pStudioData = cache->GetStudioData(handle); + studiohdr_t* pStudioHdr; if (pStudioData) { @@ -116,7 +116,8 @@ void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* { if (a3) { - pStudioData->m_Mutex.WaitForLock(); + AUTO_LOCK(pStudioData->m_Mutex); + *(_QWORD*)((int64_t)a3 + 0x880) = *(_QWORD*)&pStudioData->pad[0x24]; int64_t v6 = *(_QWORD*)&pStudioData->pad[0x24]; if (v6) @@ -124,7 +125,6 @@ void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* *(_QWORD*)&pStudioData->pad[0x24] = (int64_t)a3; *(_QWORD*)((int64_t)a3 + 0x870) = (int64_t)cache; *(_WORD*)((int64_t)a3 + 0x888) = pStudioData->m_Handle; - pStudioData->m_Mutex.ReleaseWaiter(); } } @@ -138,14 +138,13 @@ void CMDLCache::FindCachedMDL(CMDLCache* cache, studiodata_t* pStudioData, void* //----------------------------------------------------------------------------- studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, studiodata_t* pStudioData, void* a4) { - studiohdr_t* pStudioHdr; // rdi - studiohdr_t** pAnimData; // rax + AUTO_LOCK(pStudioData->m_Mutex); - pStudioData->m_Mutex.WaitForLock(); const char* szModelName = cache->GetModelName(handle); - size_t nFileNameLen = strlen(szModelName); + studiohdr_t* pStudioHdr; + if (nFileNameLen < 5 || (Q_stricmp(&szModelName[nFileNameLen - 5], ".rmdl") != 0) && (Q_stricmp(&szModelName[nFileNameLen - 5], ".rrig") != 0) && @@ -160,7 +159,6 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, st Error(eDLL_T::ENGINE, NO_ERROR, "Attempted to load old model \"%s\"; replacing with \"%s\".\n", szModelName, ERROR_MODEL); } - pStudioData->m_Mutex.ReleaseWaiter(); return pStudioHdr; } @@ -170,7 +168,7 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, st if (!pStudioData->m_MDLCache) { - pAnimData = (studiohdr_t**)pStudioData->m_pAnimData; + studiohdr_t** pAnimData = (studiohdr_t**)pStudioData->m_pAnimData; if (pAnimData) { pStudioHdr = *pAnimData; @@ -186,7 +184,6 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, st Error(eDLL_T::ENGINE, NO_ERROR, "Model \"%s\" not found; replacing with \"%s\".\n", szModelName, ERROR_MODEL); } - pStudioData->m_Mutex.ReleaseWaiter(); return pStudioHdr; } } @@ -224,7 +221,6 @@ studiohdr_t* CMDLCache::FindUncachedMDL(CMDLCache* cache, MDLHandle_t handle, st } } - pStudioData->m_Mutex.ReleaseWaiter(); return pStudioHdr; } diff --git a/r5dev/public/tier0/basetypes.h b/r5dev/public/tier0/basetypes.h index 4ed7bbb3..07a99a1d 100644 --- a/r5dev/public/tier0/basetypes.h +++ b/r5dev/public/tier0/basetypes.h @@ -375,4 +375,17 @@ protected: } -#define ExecuteOnce( x ) ExecuteNTimes( 1, x ) \ No newline at end of file +#define ExecuteOnce( x ) ExecuteNTimes( 1, x ) + +#define UID_PREFIX generated_id_ +#define UID_CAT1(a,c) a ## c +#define UID_CAT2(a,c) UID_CAT1(a,c) +#define EXPAND_CONCAT(a,c) UID_CAT1(a,c) +#ifdef _MSC_VER +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__COUNTER__) +#else +#define UNIQUE_ID UID_CAT2(UID_PREFIX,__LINE__) +#endif + +#define _MKSTRING(arg) #arg +#define MKSTRING(arg) _MKSTRING(arg) diff --git a/r5dev/public/tier0/threadtools.h b/r5dev/public/tier0/threadtools.h index 44834564..b6f5c036 100644 --- a/r5dev/public/tier0/threadtools.h +++ b/r5dev/public/tier0/threadtools.h @@ -53,11 +53,6 @@ inline void ThreadPause() #endif } -bool ThreadInMainThread(); -bool ThreadInRenderThread(); -bool ThreadInServerFrameThread(); -ThreadId_t ThreadGetCurrentId(); - //----------------------------------------------------------------------------- // // Interlock methods. These perform very fast atomic thread @@ -65,10 +60,71 @@ ThreadId_t ThreadGetCurrentId(); // //----------------------------------------------------------------------------- -int32 ThreadInterlockedCompareExchange(LONG volatile* pDest, int32 value, int32 comperand); -bool ThreadInterlockedAssignIf(LONG volatile* p, int32 value, int32 comperand); -int64 ThreadInterlockedCompareExchange64(int64 volatile* pDest, int64 value, int64 comperand); -bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value, int64 comperand); +FORCEINLINE int32 ThreadInterlockedCompareExchange(LONG volatile* pDest, int32 value, int32 comperand) +{ + return _InterlockedCompareExchange(pDest, comperand, value); +} + +FORCEINLINE bool ThreadInterlockedAssignIf(LONG volatile* p, int32 value, int32 comperand) +{ + Assert((size_t)p % 4 == 0); + return _InterlockedCompareExchange(p, comperand, value); +} + +FORCEINLINE int64 ThreadInterlockedCompareExchange64(int64 volatile* pDest, int64 value, int64 comperand) +{ + return _InterlockedCompareExchange64(pDest, comperand, value); +} + +FORCEINLINE bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value, int64 comperand) +{ + return _InterlockedCompareExchange64(pDest, comperand, value); +} + +//----------------------------------------------------------------------------- +// +// Thread checking methods. +// +//----------------------------------------------------------------------------- +#ifndef BUILDING_MATHLIB + +inline ThreadId_t* g_ThreadMainThreadID = nullptr; +inline ThreadId_t g_ThreadRenderThreadID = NULL; +inline ThreadId_t* g_ThreadServerFrameThreadID = nullptr; + +FORCEINLINE ThreadId_t ThreadGetCurrentId() +{ +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined( _PS3 ) + sys_ppu_thread_t th = 0; + sys_ppu_thread_get_id(&th); + return th; +#elif defined(POSIX) + return (ThreadId_t)pthread_self(); +#else + Assert(0); + DebuggerBreak(); + return 0; +#endif +} + +FORCEINLINE bool ThreadInMainThread() +{ + return (ThreadGetCurrentId() == (*g_ThreadMainThreadID)); +} + +FORCEINLINE bool ThreadInRenderThread() +{ + return (ThreadGetCurrentId() == g_ThreadRenderThreadID); +} + +FORCEINLINE bool ThreadInServerFrameThread() +{ + return (ThreadGetCurrentId() == (*g_ThreadServerFrameThreadID)); +} + +#endif // !BUILDING_MATHLIB #ifdef _WIN32 #define NOINLINE @@ -204,31 +260,17 @@ typedef CInterlockedIntT CInterlockedUInt; #ifndef BUILDING_MATHLIB //============================================================================= -class CThreadFastMutex; - -inline CMemory p_MutexInternal_WaitForLock; -inline auto v_MutexInternal_WaitForLock = p_MutexInternal_WaitForLock.RCast(); - -inline CMemory p_MutexInternal_ReleaseWaiter; -inline auto v_MutexInternal_ReleaseWaiter = p_MutexInternal_ReleaseWaiter.RCast(); - inline CMemory p_DeclareCurrentThreadIsMainThread; inline auto v_DeclareCurrentThreadIsMainThread = p_DeclareCurrentThreadIsMainThread.RCast(); -inline ThreadId_t* g_ThreadMainThreadID = nullptr; -inline ThreadId_t g_ThreadRenderThreadID = NULL; -inline ThreadId_t* g_ThreadServerFrameThreadID = nullptr; +#endif // !BUILDING_MATHLIB /////////////////////////////////////////////////////////////////////////////// class CThreadFastMutex { public: - int WaitForLock(void) { - return v_MutexInternal_WaitForLock(this); - } - int ReleaseWaiter(void) { - return v_MutexInternal_ReleaseWaiter(this); - } + int Lock(void); + int Unlock(void); inline uint32 GetOwnerId(void) const { return m_nOwnerID; } inline int GetDepth(void) const { return m_nDepth; } @@ -242,25 +284,65 @@ private: HANDLE m_hSemaphore; }; +/////////////////////////////////////////////////////////////////////////////// +template +class CAutoLockT +{ +public: + FORCEINLINE CAutoLockT(MUTEX_TYPE& lock) + : m_lock(lock) + { + m_lock.Lock(); + } + + FORCEINLINE CAutoLockT(const MUTEX_TYPE& lock) + : m_lock(const_cast(lock)) + { + m_lock.Lock(); + } + + FORCEINLINE ~CAutoLockT() + { + m_lock.Unlock(); + } + + +private: + MUTEX_TYPE& m_lock; + + // Disallow copying + CAutoLockT(const CAutoLockT&); + CAutoLockT& operator=(const CAutoLockT&); +}; + +/////////////////////////////////////////////////////////////////////////////// +template struct CAutoLockTypeDeducer {}; +template <> struct CAutoLockTypeDeducer { typedef CThreadFastMutex Type_t; }; + +#define AUTO_LOCK_( type, mutex ) \ + CAutoLockT< type > UNIQUE_ID( static_cast( mutex ) ) + +#if defined(__clang__) +#define AUTO_LOCK(mutex) \ + AUTO_LOCK_(typename CAutoLockTypeDeducer::Type_t, mutex) +#else +#define AUTO_LOCK(mutex) \ + AUTO_LOCK_(CAutoLockTypeDeducer::Type_t, mutex) +#endif + +#ifndef BUILDING_MATHLIB /////////////////////////////////////////////////////////////////////////////// class VThreadTools : public IDetour { virtual void GetAdr(void) const { - LogFunAdr("CThreadFastMutex::WaitForLock", p_MutexInternal_WaitForLock.GetPtr()); - LogFunAdr("CThreadFastMutex::ReleaseWaiter", p_MutexInternal_ReleaseWaiter.GetPtr()); LogFunAdr("DeclareCurrentThreadIsMainThread", p_DeclareCurrentThreadIsMainThread.GetPtr()); LogVarAdr("g_ThreadMainThreadID", reinterpret_cast(g_ThreadMainThreadID)); LogVarAdr("g_ThreadServerFrameThreadID", reinterpret_cast(g_ThreadServerFrameThreadID)); } virtual void GetFun(void) const { - p_MutexInternal_WaitForLock = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B D9 FF 15 ?? ?? ?? ??"); - p_MutexInternal_ReleaseWaiter = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B 41 04 48 8B D9 83 E8 01"); p_DeclareCurrentThreadIsMainThread = g_GameDll.FindPatternSIMD("48 83 EC 28 FF 15 ?? ?? ?? ?? 89 05 ?? ?? ?? ?? 48 83 C4 28"); - - v_MutexInternal_WaitForLock = p_MutexInternal_WaitForLock.RCast(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B D9 FF 15 ?? ?? ?? ??*/ - v_MutexInternal_ReleaseWaiter = p_MutexInternal_ReleaseWaiter.RCast(); /*40 53 48 83 EC 20 8B 41 04 48 8B D9 83 E8 01*/ v_DeclareCurrentThreadIsMainThread = p_DeclareCurrentThreadIsMainThread.RCast(); /*48 83 EC 28 FF 15 ?? ?? ?? ?? 89 05 ?? ?? ?? ?? 48 83 C4 28 */ } virtual void GetVar(void) const @@ -275,5 +357,4 @@ class VThreadTools : public IDetour /////////////////////////////////////////////////////////////////////////////// #endif // !BUILDING_MATHLIB - #endif // THREADTOOLS_H diff --git a/r5dev/tier0/threadtools.cpp b/r5dev/tier0/threadtools.cpp index 01b0a6be..0972a0c3 100644 --- a/r5dev/tier0/threadtools.cpp +++ b/r5dev/tier0/threadtools.cpp @@ -8,55 +8,98 @@ #include "tier0/threadtools.h" -int32 ThreadInterlockedCompareExchange(LONG volatile* pDest, int32 value, int32 comperand) +#define INIT_SEM_COUNT 0 +#define MAX_SEM_COUNT 1 + +int CThreadFastMutex::Lock(void) { - return _InterlockedCompareExchange(pDest, comperand, value); + DWORD threadId = GetCurrentThreadId(); + LONG result = ThreadInterlockedCompareExchange((volatile LONG*)&m_lAddend, 0, 1); + + if (result) + { + if (m_nOwnerID == threadId) + { + result = m_nDepth + 1; + m_nDepth = result; + + return result; + } + + LONG cycle = 1; + LONG64 delay; + + while (true) + { + delay = (10 * cycle); + if (delay) + { + do + { + ThreadPause(); + --delay; + } while (delay); + } + + result = ThreadInterlockedCompareExchange((volatile LONG*)&m_lAddend, 0, 1); + + if (result) + break; + + if (++cycle > 5) + { + if (_InterlockedIncrement((volatile LONG*)&m_lAddend) != 1) + { + if (!m_hSemaphore) + { + HANDLE hSemaphore = CreateSemaphoreA( + NULL, INIT_SEM_COUNT, MAX_SEM_COUNT, NULL); + + if (ThreadInterlockedCompareExchange64( + (volatile LONG64*)&m_hSemaphore, NULL, (LONG64)hSemaphore)) + CloseHandle(hSemaphore); + } + WaitForSingleObject(m_hSemaphore, INFINITE); + } + m_nOwnerID = threadId; + m_nDepth = 1; + + return m_nDepth; + } + } + } + + m_nDepth = 1; + m_nOwnerID = threadId; + + return result; } -bool ThreadInterlockedAssignIf(LONG volatile* p, int32 value, int32 comperand) +int CThreadFastMutex::Unlock() { - Assert((size_t)p % 4 == 0); - return _InterlockedCompareExchange(p, comperand, value); -} + LONG result; // eax + HANDLE SemaphoreA; // rcx -int64 ThreadInterlockedCompareExchange64(int64 volatile* pDest, int64 value, int64 comperand) -{ - return _InterlockedCompareExchange64(pDest, comperand, value); -} + result = m_nDepth - 1; + m_nDepth = result; -bool ThreadInterlockedAssignIf64(int64 volatile* pDest, int64 value, int64 comperand) -{ - return _InterlockedCompareExchange64(pDest, comperand, value); -} + if (!result) + { + m_nOwnerID = 0; + result = _InterlockedExchangeAdd((volatile LONG*)&m_lAddend, 0xFFFFFFFF); -bool ThreadInMainThread() -{ - return (ThreadGetCurrentId() == (*g_ThreadMainThreadID)); -} + if (result != 1) + { + if (!m_hSemaphore) + { + SemaphoreA = CreateSemaphoreA(NULL, INIT_SEM_COUNT, MAX_SEM_COUNT, NULL); -bool ThreadInRenderThread() -{ - return (ThreadGetCurrentId() == g_ThreadRenderThreadID); + if (ThreadInterlockedAssignIf64( + (volatile LONG64*)&m_hSemaphore, NULL, (LONG64)SemaphoreA)) + CloseHandle(SemaphoreA); + } + return ReleaseSemaphore(m_hSemaphore, 1, NULL); + } + } + return result; } - -bool ThreadInServerFrameThread() -{ - return (ThreadGetCurrentId() == (*g_ThreadServerFrameThreadID)); -} - -ThreadId_t ThreadGetCurrentId() -{ -#ifdef _WIN32 - return GetCurrentThreadId(); -#elif defined( _PS3 ) - sys_ppu_thread_t th = 0; - sys_ppu_thread_get_id(&th); - return th; -#elif defined(POSIX) - return (ThreadId_t)pthread_self(); -#else - Assert(0); - DebuggerBreak(); - return 0; -#endif -} \ No newline at end of file