[Base] Replace CRITICAL_SECTION with SRWLOCK based mutexes

xe_fast_mutex now also asserts on recursive lock attempts
This commit is contained in:
Herman S.
2026-02-10 10:10:03 +09:00
parent de63bb18a6
commit 77535b7cec
2 changed files with 62 additions and 55 deletions

View File

@@ -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<CRITICAL_SECTION*>(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<CRITICAL_SECTION*>(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() {

View File

@@ -10,27 +10,27 @@
#ifndef XENIA_BASE_MUTEX_H_
#define XENIA_BASE_MUTEX_H_
#include <mutex>
#include "memory.h"
#include <thread>
#include "platform.h"
#if XE_PLATFORM_WIN32
#include "platform_win.h"
#elif XE_PLATFORM_LINUX
#include <sys/types.h>
#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();