From 77535b7cecf33d58b98294fae304b24bb6c2940a Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Tue, 10 Feb 2026 10:10:03 +0900 Subject: [PATCH] [Base] Replace CRITICAL_SECTION with SRWLOCK based mutexes xe_fast_mutex now also asserts on recursive lock attempts --- src/xenia/base/mutex.cc | 82 ++++++++++++++++++++++------------------- src/xenia/base/mutex.h | 35 +++++++++--------- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/src/xenia/base/mutex.cc b/src/xenia/base/mutex.cc index 4fc37de9b..5f62ae39b 100644 --- a/src/xenia/base/mutex.cc +++ b/src/xenia/base/mutex.cc @@ -14,55 +14,61 @@ namespace xe { #if XE_PLATFORM_WIN32 == 1 && XE_ENABLE_FAST_WIN32_MUTEX == 1 -// default spincount for entercriticalsection is insane on windows, 0x20007D0i64 -// (33556432 times!!) when a lock is highly contended performance degrades -// sharply on some processors todo: perhaps we should have a set of optional -// jobs that processors can do instead of spinning, for instance, sorting a list -// so we have better locality later or something -#define XE_CRIT_SPINCOUNT 128 -/* -chrispy: todo, if a thread exits before releasing the global mutex we need to -check this and release the mutex one way to do this is by using FlsAlloc and -PFLS_CALLBACK_FUNCTION, which gets called with the fiber local data when a -thread exits -*/ - -static CRITICAL_SECTION* global_critical_section(xe_global_mutex* mutex) { - return reinterpret_cast(mutex); -} - -xe_global_mutex::xe_global_mutex() { - InitializeCriticalSectionEx(global_critical_section(this), XE_CRIT_SPINCOUNT, - CRITICAL_SECTION_NO_DEBUG_INFO); -} -xe_global_mutex ::~xe_global_mutex() { - DeleteCriticalSection(global_critical_section(this)); -} +// xe_global_mutex: recursive mutex via SRWLOCK void xe_global_mutex::lock() { - EnterCriticalSection(global_critical_section(this)); + DWORD self = GetCurrentThreadId(); + if (owner_thread_ == self) { + ++recursion_count_; + return; + } + AcquireSRWLockExclusive(&srwlock_); + owner_thread_ = self; + recursion_count_ = 1; } + void xe_global_mutex::unlock() { - LeaveCriticalSection(global_critical_section(this)); + if (--recursion_count_ == 0) { + owner_thread_ = 0; + ReleaseSRWLockExclusive(&srwlock_); + } } + bool xe_global_mutex::try_lock() { - BOOL success = TryEnterCriticalSection(global_critical_section(this)); - return success; + DWORD self = GetCurrentThreadId(); + if (owner_thread_ == self) { + ++recursion_count_; + return true; + } + if (TryAcquireSRWLockExclusive(&srwlock_)) { + owner_thread_ = self; + recursion_count_ = 1; + return true; + } + return false; } -CRITICAL_SECTION* fast_crit(xe_fast_mutex* mutex) { - return reinterpret_cast(mutex); +// xe_fast_mutex: non-recursive mutex via SRWLOCK +void xe_fast_mutex::lock() { + DWORD self = GetCurrentThreadId(); + if (owner_thread_ == self) { + assert_always("xe_fast_mutex: recursive lock detected"); + } + AcquireSRWLockExclusive(&srwlock_); + owner_thread_ = self; +} + +void xe_fast_mutex::unlock() { + owner_thread_ = 0; + ReleaseSRWLockExclusive(&srwlock_); } -xe_fast_mutex::xe_fast_mutex() { - InitializeCriticalSectionEx(fast_crit(this), XE_CRIT_SPINCOUNT, - CRITICAL_SECTION_NO_DEBUG_INFO); -} -xe_fast_mutex::~xe_fast_mutex() { DeleteCriticalSection(fast_crit(this)); } -void xe_fast_mutex::lock() { EnterCriticalSection(fast_crit(this)); } -void xe_fast_mutex::unlock() { LeaveCriticalSection(fast_crit(this)); } bool xe_fast_mutex::try_lock() { - return TryEnterCriticalSection(fast_crit(this)); + if (TryAcquireSRWLockExclusive(&srwlock_)) { + owner_thread_ = GetCurrentThreadId(); + return true; + } + return false; } #endif global_mutex_type& global_critical_region::mutex() { diff --git a/src/xenia/base/mutex.h b/src/xenia/base/mutex.h index d5e691c60..665f38f97 100644 --- a/src/xenia/base/mutex.h +++ b/src/xenia/base/mutex.h @@ -10,27 +10,27 @@ #ifndef XENIA_BASE_MUTEX_H_ #define XENIA_BASE_MUTEX_H_ #include -#include "memory.h" +#include #include "platform.h" +#if XE_PLATFORM_WIN32 +#include "platform_win.h" +#elif XE_PLATFORM_LINUX +#include +#endif +#include "memory.h" #define XE_ENABLE_FAST_WIN32_MUTEX 1 namespace xe { #if XE_PLATFORM_WIN32 == 1 && XE_ENABLE_FAST_WIN32_MUTEX == 1 -/* - must conform to - BasicLockable:https://en.cppreference.com/w/cpp/named_req/BasicLockable as - well as Lockable: https://en.cppreference.com/w/cpp/named_req/Lockable - - this emulates a recursive mutex, except with far less overhead -*/ - +// Recursive mutex using SRWLOCK. class alignas(4096) xe_global_mutex { - XE_MAYBE_UNUSED - char detail[64]; + SRWLOCK srwlock_ = SRWLOCK_INIT; + DWORD owner_thread_ = 0; + uint32_t recursion_count_ = 0; public: - xe_global_mutex(); - ~xe_global_mutex(); + xe_global_mutex() = default; + ~xe_global_mutex() = default; void lock(); void unlock(); @@ -38,13 +38,14 @@ class alignas(4096) xe_global_mutex { }; using global_mutex_type = xe_global_mutex; +// Non-recursive mutex using SRWLOCK. class alignas(64) xe_fast_mutex { - XE_MAYBE_UNUSED - char detail[64]; + SRWLOCK srwlock_ = SRWLOCK_INIT; + DWORD owner_thread_ = 0; public: - xe_fast_mutex(); - ~xe_fast_mutex(); + xe_fast_mutex() = default; + ~xe_fast_mutex() = default; void lock(); void unlock();