RetroAchievements / rcheevos

Library to parse and evaluate achievements and leaderboards for RetroAchievements
MIT License
90 stars 34 forks source link

Use better Win32 mutexes for rc_mutex_t #376

Closed CasualPokePlayer closed 1 month ago

CasualPokePlayer commented 1 month ago

https://learn.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c

On Windows, a simple Mutex HANDLE (using Mutex functions) seems like an appropriate implementation for rc_mutex_t. While it does work, it is horribly inefficient and actually overkill, as on Win32, a Mutex is a synchronization object capable of inter-process locking, thus requiring entering kernel code always when using a Mutex function (going into kernel code implicitly being fairly expensive).

A critical section works similarly, except it is not capable of inter-process locking. This is much better for rc_mutex_t, which is obviously not expecting to be used in a different process. Critical sections are also capable of spinlooping for a little bit of time before trying to acquire the critical section, thus potentially avoiding an expensive system call when contention goes away quickly.

Windows Vista introduced slim reader/writer (SRW) locks. These are very lightweight, mostly implemented in user mode (only going to kernel if it decides it should just outright sleep), and using newer (more efficient) "keyed event objects" (which only have 1 actual kernel object allocated for all objects, only used as a last resort for critical sections due to backwards compatibility reasons). They aren't a direct replacement for critical sections as they are incapable of recursive acquires, but that's easy enough to implement by tracking the current thread owner.

In case code is compiled with Vista or later as the minimum version (WINVER >= 0x0600) then SRW locks will be used, otherwise critical sections will be used.

This commit also does a minor fix in rc_compat.h with libogc mutexes, as previously it was just including pthread.h and typedef'ing rc_mutex_t against pthread_mutex_t. Not sure why this ever compiled exactly (does that indicate GC/Wii can just use pthread mutexes? Or was RC_NO_THREADS actually being defined?), but regardless I've changed it to properly typedef against libogc's mutex_t.