Create a global event lock for hardware that generates events

This prevents ABBA deadlocks caused by taking a hardware resource lock then delivering events at the same time another thread is taking a hardware resource lock from an event watch callback.

Fixes https://github.com/libsdl-org/SDL/issues/15709
This commit is contained in:
Sam Lantinga
2026-06-02 15:29:06 -07:00
parent 75270a4264
commit f9d49358d2
18 changed files with 99 additions and 172 deletions

View File

@@ -75,8 +75,8 @@
} while (0)
static bool SDL_gamepads_initialized;
static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_HashTable *SDL_gamepad_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_Gamepad *SDL_gamepads SDL_GUARDED_BY(SDL_event_lock) = NULL;
static SDL_HashTable *SDL_gamepad_names SDL_GUARDED_BY(SDL_event_lock) = NULL;
// The face button style of a gamepad
typedef enum
@@ -96,7 +96,7 @@ typedef enum
SDL_GAMEPAD_MAPPING_PRIORITY_USER,
} SDL_GamepadMappingPriority;
#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
#define _guarded SDL_GUARDED_BY(SDL_event_lock)
typedef struct GamepadMapping_t
{
@@ -121,13 +121,13 @@ typedef struct
#undef _guarded
static SDL_GUID s_zeroGUID;
static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_HashTable *s_gamepadInstanceIDs SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static GamepadMapping_t *s_pSupportedGamepads SDL_GUARDED_BY(SDL_event_lock) = NULL;
static GamepadMapping_t *s_pDefaultMapping SDL_GUARDED_BY(SDL_event_lock) = NULL;
static GamepadMapping_t *s_pXInputMapping SDL_GUARDED_BY(SDL_event_lock) = NULL;
static MappingChangeTracker *s_mappingChangeTracker SDL_GUARDED_BY(SDL_event_lock) = NULL;
static SDL_HashTable *s_gamepadInstanceIDs SDL_GUARDED_BY(SDL_event_lock) = NULL;
#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
#define _guarded SDL_GUARDED_BY(SDL_event_lock)
// The SDL gamepad structure
struct SDL_Gamepad

View File

@@ -114,19 +114,14 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#endif
};
#ifndef SDL_THREAD_SAFETY_ANALYSIS
static
#endif
SDL_Mutex *SDL_joystick_lock = NULL; // This needs to support recursive locks
static SDL_AtomicInt SDL_joystick_lock_pending;
static int SDL_joysticks_locked;
static bool SDL_joysticks_initialized;
static bool SDL_joysticks_quitting;
static bool SDL_joystick_being_added;
static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_Joystick *SDL_joysticks SDL_GUARDED_BY(SDL_event_lock) = NULL;
static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_event_lock) = 0;
static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_event_lock) = NULL;
static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_event_lock) = NULL;
static bool SDL_joystick_allows_background_events = false;
static Uint32 initial_old_xboxone_controllers[] = {
@@ -706,16 +701,13 @@ bool SDL_JoysticksQuitting(void)
void SDL_LockJoysticks(void)
{
(void)SDL_AtomicIncRef(&SDL_joystick_lock_pending);
SDL_LockMutex(SDL_joystick_lock);
(void)SDL_AtomicDecRef(&SDL_joystick_lock_pending);
SDL_LockMutex(SDL_event_lock);
++SDL_joysticks_locked;
}
bool SDL_TryLockJoysticks(void)
{
if (SDL_TryLockMutex(SDL_joystick_lock)) {
if (SDL_TryLockMutex(SDL_event_lock)) {
++SDL_joysticks_locked;
return true;
}
@@ -724,34 +716,8 @@ bool SDL_TryLockJoysticks(void)
void SDL_UnlockJoysticks(void)
{
bool last_unlock = false;
--SDL_joysticks_locked;
if (!SDL_joysticks_initialized) {
// NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks
if (!SDL_joysticks_locked && SDL_GetAtomicInt(&SDL_joystick_lock_pending) == 0) {
last_unlock = true;
}
}
/* The last unlock after joysticks are uninitialized will cleanup the mutex,
* allowing applications to lock joysticks while reinitializing the system.
*/
if (last_unlock) {
SDL_Mutex *joystick_lock = SDL_joystick_lock;
SDL_LockMutex(joystick_lock);
{
SDL_UnlockMutex(SDL_joystick_lock);
SDL_joystick_lock = NULL;
}
SDL_UnlockMutex(joystick_lock);
SDL_DestroyMutex(joystick_lock);
} else {
SDL_UnlockMutex(SDL_joystick_lock);
}
SDL_UnlockMutex(SDL_event_lock);
}
bool SDL_JoysticksLocked(void)
@@ -892,11 +858,6 @@ bool SDL_InitJoysticks(void)
int i;
bool result = false;
// Create the joystick list lock
if (SDL_joystick_lock == NULL) {
SDL_joystick_lock = SDL_CreateMutex();
}
if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
return false;
}

View File

@@ -48,7 +48,7 @@ extern bool SDL_JoysticksQuitting(void);
extern bool SDL_JoysticksLocked(void);
// Make sure we currently have the joysticks locked
extern void SDL_AssertJoysticksLocked(void) SDL_ASSERT_CAPABILITY(SDL_joystick_lock);
extern void SDL_AssertJoysticksLocked(void) SDL_ASSERT_CAPABILITY(SDL_event_lock);
// Function to return whether there are any joysticks opened by the application
extern bool SDL_JoysticksOpened(void);

View File

@@ -33,11 +33,11 @@
#include <sys/stat.h>
#endif
static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_event_lock) = NULL;
static Uint64 SDL_steam_virtual_gamepad_info_file_mtime SDL_GUARDED_BY(SDL_event_lock) = 0;
static Uint64 SDL_steam_virtual_gamepad_info_check_time SDL_GUARDED_BY(SDL_event_lock) = 0;
static SDL_SteamVirtualGamepadInfo **SDL_steam_virtual_gamepad_info SDL_GUARDED_BY(SDL_event_lock) = NULL;
static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_event_lock) = 0;
static Uint64 GetFileModificationTime(const char *file)

View File

@@ -25,6 +25,7 @@
// This is the system specific header for the SDL joystick API
#include "SDL_joystick_c.h"
#include "../events/SDL_events_c.h"
// Set up for C function definitions, even when using C++
#ifdef __cplusplus
@@ -78,7 +79,7 @@ typedef struct SDL_JoystickCapSenseInfo
bool down;
} SDL_JoystickCapSenseInfo;
#define _guarded SDL_GUARDED_BY(SDL_joystick_lock)
#define _guarded SDL_GUARDED_BY(SDL_event_lock)
struct SDL_Joystick
{

View File

@@ -117,7 +117,7 @@ static int SDL_HIDAPI_numdrivers = 0;
static SDL_AtomicInt SDL_HIDAPI_updating_devices;
static bool SDL_HIDAPI_hints_changed = false;
static Uint32 SDL_HIDAPI_change_count = 0;
static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_joystick_lock);
static SDL_HIDAPI_Device *SDL_HIDAPI_devices SDL_GUARDED_BY(SDL_event_lock);
static int SDL_HIDAPI_numjoysticks = 0;
static bool SDL_HIDAPI_combine_joycons = true;
static bool initialized = false;

View File

@@ -186,10 +186,10 @@ typedef struct SDL_sensorlist_item
} SDL_sensorlist_item;
static bool SDL_classic_joysticks = false;
static SDL_joylist_item *SDL_joylist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_joylist_item *SDL_joylist_tail SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static int numjoysticks SDL_GUARDED_BY(SDL_joystick_lock) = 0;
static SDL_sensorlist_item *SDL_sensorlist SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static SDL_joylist_item *SDL_joylist SDL_GUARDED_BY(SDL_event_lock) = NULL;
static SDL_joylist_item *SDL_joylist_tail SDL_GUARDED_BY(SDL_event_lock) = NULL;
static int numjoysticks SDL_GUARDED_BY(SDL_event_lock) = 0;
static SDL_sensorlist_item *SDL_sensorlist SDL_GUARDED_BY(SDL_event_lock) = NULL;
static int inotify_fd = -1;
static Uint64 last_joy_detect_time;

View File

@@ -28,7 +28,7 @@
#include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.h"
static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_joystick_lock) = NULL;
static joystick_hwdata *g_VJoys SDL_GUARDED_BY(SDL_event_lock) = NULL;
static joystick_hwdata *VIRTUAL_HWDataForInstance(SDL_JoystickID instance_id)
{