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

@ -81,7 +81,7 @@ extern "C" {
* help Clang's thread safety analysis tools to function. Do not attempt * help Clang's thread safety analysis tools to function. Do not attempt
* to access this symbol from your app, it will not work! * to access this symbol from your app, it will not work!
*/ */
extern SDL_Mutex *SDL_joystick_lock; extern SDL_Mutex *SDL_event_lock;
#endif #endif
/** /**
@ -186,7 +186,7 @@ typedef enum SDL_JoystickConnectionState
* *
* \since This function is available since SDL 3.2.0. * \since This function is available since SDL 3.2.0.
*/ */
extern SDL_DECLSPEC void SDLCALL SDL_LockJoysticks(void) SDL_ACQUIRE(SDL_joystick_lock); extern SDL_DECLSPEC void SDLCALL SDL_LockJoysticks(void) SDL_ACQUIRE(SDL_event_lock);
/** /**
* Locking for atomic access to the joystick API. * Locking for atomic access to the joystick API.
@ -201,7 +201,7 @@ extern SDL_DECLSPEC void SDLCALL SDL_LockJoysticks(void) SDL_ACQUIRE(SDL_joystic
* *
* \since This function is available since SDL 3.6.0. * \since This function is available since SDL 3.6.0.
*/ */
extern SDL_DECLSPEC bool SDLCALL SDL_TryLockJoysticks(void) SDL_ACQUIRE(SDL_joystick_lock); extern SDL_DECLSPEC bool SDLCALL SDL_TryLockJoysticks(void) SDL_TRY_ACQUIRE(true, SDL_event_lock);
/** /**
* Unlocking for atomic access to the joystick API. * Unlocking for atomic access to the joystick API.
@ -211,7 +211,7 @@ extern SDL_DECLSPEC bool SDLCALL SDL_TryLockJoysticks(void) SDL_ACQUIRE(SDL_joys
* *
* \since This function is available since SDL 3.2.0. * \since This function is available since SDL 3.2.0.
*/ */
extern SDL_DECLSPEC void SDLCALL SDL_UnlockJoysticks(void) SDL_RELEASE(SDL_joystick_lock); extern SDL_DECLSPEC void SDLCALL SDL_UnlockJoysticks(void) SDL_RELEASE(SDL_event_lock);
/** /**
* Return whether a joystick is currently connected. * Return whether a joystick is currently connected.

View file

@ -298,6 +298,7 @@ void SDL_InitMainThread(void)
SDL_InitEnvironment(); SDL_InitEnvironment();
SDL_InitTicks(); SDL_InitTicks();
SDL_InitFilesystem(); SDL_InitFilesystem();
SDL_CreateEventLock();
if (!done_info) { if (!done_info) {
const char *value; const char *value;
@ -316,6 +317,7 @@ void SDL_InitMainThread(void)
static void SDL_QuitMainThread(void) static void SDL_QuitMainThread(void)
{ {
SDL_DestroyEventLock();
SDL_QuitFilesystem(); SDL_QuitFilesystem();
SDL_QuitTicks(); SDL_QuitTicks();
SDL_QuitEnvironment(); SDL_QuitEnvironment();

View file

@ -161,6 +161,23 @@ static struct
} SDL_EventQ = { NULL, false, { 0 }, 0, NULL, NULL, NULL }; } SDL_EventQ = { NULL, false, { 0 }, 0, NULL, NULL, NULL };
SDL_Mutex *SDL_event_lock = NULL; // This needs to support recursive locks
void SDL_CreateEventLock(void)
{
if (!SDL_event_lock) {
SDL_event_lock = SDL_CreateMutex();
}
}
void SDL_DestroyEventLock(void)
{
if (SDL_event_lock) {
SDL_DestroyMutex(SDL_event_lock);
SDL_event_lock = NULL;
}
}
static void SDL_CleanupTemporaryMemory(void *data) static void SDL_CleanupTemporaryMemory(void *data)
{ {
SDL_TemporaryMemoryState *state = (SDL_TemporaryMemoryState *)data; SDL_TemporaryMemoryState *state = (SDL_TemporaryMemoryState *)data;
@ -965,8 +982,9 @@ void SDL_StopEventLoop(void)
const char *report = SDL_GetHint("SDL_EVENT_QUEUE_STATISTICS"); const char *report = SDL_GetHint("SDL_EVENT_QUEUE_STATISTICS");
int i; int i;
SDL_EventEntry *entry; SDL_EventEntry *entry;
SDL_Mutex *lock = SDL_EventQ.lock;
SDL_LockMutex(SDL_EventQ.lock); SDL_LockMutex(lock);
SDL_EventQ.active = false; SDL_EventQ.active = false;
@ -1004,17 +1022,10 @@ void SDL_StopEventLoop(void)
SDL_QuitEventWatchList(&SDL_event_watchers); SDL_QuitEventWatchList(&SDL_event_watchers);
SDL_QuitWindowEventWatch(); SDL_QuitWindowEventWatch();
SDL_Mutex *lock = NULL; SDL_EventQ.lock = NULL;
if (SDL_EventQ.lock) {
lock = SDL_EventQ.lock;
SDL_EventQ.lock = NULL;
}
SDL_UnlockMutex(lock); SDL_UnlockMutex(lock);
SDL_DestroyMutex(lock);
if (lock) {
SDL_DestroyMutex(lock);
}
} }
// This function (and associated calls) may be called more than once // This function (and associated calls) may be called more than once
@ -1833,7 +1844,7 @@ bool SDL_PushEvent(SDL_Event *event)
void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata) void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
{ {
SDL_EventEntry *event, *next; SDL_EventEntry *event, *next;
SDL_LockMutex(SDL_event_watchers.lock); SDL_LockMutex(SDL_event_lock);
{ {
// Set filter and discard pending events // Set filter and discard pending events
SDL_event_watchers.filter.callback = filter; SDL_event_watchers.filter.callback = filter;
@ -1852,18 +1863,18 @@ void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
SDL_UnlockMutex(SDL_EventQ.lock); SDL_UnlockMutex(SDL_EventQ.lock);
} }
} }
SDL_UnlockMutex(SDL_event_watchers.lock); SDL_UnlockMutex(SDL_event_lock);
} }
bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata) bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata)
{ {
SDL_EventWatcher event_ok; SDL_EventWatcher event_ok;
SDL_LockMutex(SDL_event_watchers.lock); SDL_LockMutex(SDL_event_lock);
{ {
event_ok = SDL_event_watchers.filter; event_ok = SDL_event_watchers.filter;
} }
SDL_UnlockMutex(SDL_event_watchers.lock); SDL_UnlockMutex(SDL_event_lock);
if (filter) { if (filter) {
*filter = event_ok.callback; *filter = event_ok.callback;

View file

@ -36,6 +36,15 @@
#include "SDL_pen_c.h" #include "SDL_pen_c.h"
#include "SDL_windowevents_c.h" #include "SDL_windowevents_c.h"
// The event mutex
//
// This mutex prevents multiple threads from watching multiple events
// simultaneously and also protects resources like joysticks that may
// be accessed from multiple threads and also generate events.
extern SDL_Mutex *SDL_event_lock;
extern void SDL_CreateEventLock(void);
extern void SDL_DestroyEventLock(void);
// Start and stop the event processing loop // Start and stop the event processing loop
extern bool SDL_StartEventLoop(void); extern bool SDL_StartEventLoop(void);
extern void SDL_StopEventLoop(void); extern void SDL_StopEventLoop(void);

View file

@ -21,25 +21,16 @@
#include "SDL_internal.h" #include "SDL_internal.h"
#include "SDL_eventwatch_c.h" #include "SDL_eventwatch_c.h"
#include "SDL_events_c.h"
bool SDL_InitEventWatchList(SDL_EventWatchList *list) bool SDL_InitEventWatchList(SDL_EventWatchList *list)
{ {
if (list->lock == NULL) {
list->lock = SDL_CreateMutex();
if (list->lock == NULL) {
return false;
}
}
return true; return true;
} }
void SDL_QuitEventWatchList(SDL_EventWatchList *list) void SDL_QuitEventWatchList(SDL_EventWatchList *list)
{ {
if (list->lock) {
SDL_DestroyMutex(list->lock);
list->lock = NULL;
}
if (list->watchers) { if (list->watchers) {
SDL_free(list->watchers); SDL_free(list->watchers);
list->watchers = NULL; list->watchers = NULL;
@ -56,13 +47,13 @@ bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event)
return true; return true;
} }
SDL_LockMutex(list->lock); SDL_LockMutex(SDL_event_lock);
{ {
// Make sure we only dispatch the current watcher list // Make sure we only dispatch the current watcher list
int i, count = list->count; int i, count = list->count;
if (filter->callback && !filter->callback(filter->userdata, event)) { if (filter->callback && !filter->callback(filter->userdata, event)) {
SDL_UnlockMutex(list->lock); SDL_UnlockMutex(SDL_event_lock);
return false; return false;
} }
@ -86,7 +77,7 @@ bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event)
list->removed = false; list->removed = false;
} }
} }
SDL_UnlockMutex(list->lock); SDL_UnlockMutex(SDL_event_lock);
return true; return true;
} }
@ -95,7 +86,7 @@ bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, voi
{ {
bool result = true; bool result = true;
SDL_LockMutex(list->lock); SDL_LockMutex(SDL_event_lock);
{ {
SDL_EventWatcher *watchers; SDL_EventWatcher *watchers;
@ -113,14 +104,14 @@ bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, voi
result = false; result = false;
} }
} }
SDL_UnlockMutex(list->lock); SDL_UnlockMutex(SDL_event_lock);
return result; return result;
} }
void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata) void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata)
{ {
SDL_LockMutex(list->lock); SDL_LockMutex(SDL_event_lock);
{ {
int i; int i;
@ -139,5 +130,5 @@ void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter,
} }
} }
} }
SDL_UnlockMutex(list->lock); SDL_UnlockMutex(SDL_event_lock);
} }

View file

@ -29,7 +29,6 @@ typedef struct SDL_EventWatcher
typedef struct SDL_EventWatchList typedef struct SDL_EventWatchList
{ {
SDL_Mutex *lock;
SDL_EventWatcher filter; SDL_EventWatcher filter;
SDL_EventWatcher *watchers; SDL_EventWatcher *watchers;
int count; int count;

View file

@ -25,21 +25,20 @@
#include "SDL_events_c.h" #include "SDL_events_c.h"
#include "../video/SDL_sysvideo.h" #include "../video/SDL_sysvideo.h"
static SDL_Mutex *SDL_touch_lock = NULL; // This needs to support recursive locks
static int SDL_touch_locked = 0; static int SDL_touch_locked = 0;
struct SDL_Touch struct SDL_Touch
{ {
SDL_TouchID id SDL_GUARDED_BY(SDL_touch_lock); SDL_TouchID id SDL_GUARDED_BY(SDL_event_lock);
SDL_TouchDeviceType type SDL_GUARDED_BY(SDL_touch_lock); SDL_TouchDeviceType type SDL_GUARDED_BY(SDL_event_lock);
int num_fingers SDL_GUARDED_BY(SDL_touch_lock); int num_fingers SDL_GUARDED_BY(SDL_event_lock);
int max_fingers SDL_GUARDED_BY(SDL_touch_lock); int max_fingers SDL_GUARDED_BY(SDL_event_lock);
SDL_Finger **fingers SDL_GUARDED_BY(SDL_touch_lock); SDL_Finger **fingers SDL_GUARDED_BY(SDL_event_lock);
char *name SDL_GUARDED_BY(SDL_touch_lock); char *name SDL_GUARDED_BY(SDL_event_lock);
}; };
static int SDL_num_touch SDL_GUARDED_BY(SDL_touch_lock) = 0; static int SDL_num_touch SDL_GUARDED_BY(SDL_event_lock) = 0;
static SDL_Touch **SDL_touchDevices SDL_GUARDED_BY(SDL_touch_lock) = NULL; static SDL_Touch **SDL_touchDevices SDL_GUARDED_BY(SDL_event_lock) = NULL;
// for mapping touch events to mice // for mapping touch events to mice
static bool finger_touching = false; static bool finger_touching = false;
@ -49,23 +48,22 @@ static SDL_TouchID track_touchid;
// Public functions // Public functions
bool SDL_InitTouch(void) bool SDL_InitTouch(void)
{ {
SDL_touch_lock = SDL_CreateMutex();
return true; return true;
} }
static void SDL_LockTouch(void) SDL_ACQUIRE(SDL_touch_lock) static void SDL_LockTouch(void) SDL_ACQUIRE(SDL_event_lock)
{ {
SDL_LockMutex(SDL_touch_lock); SDL_LockMutex(SDL_event_lock);
++SDL_touch_locked; ++SDL_touch_locked;
} }
static void SDL_UnlockTouch(void) SDL_RELEASE(SDL_touch_lock) static void SDL_UnlockTouch(void) SDL_RELEASE(SDL_event_lock)
{ {
--SDL_touch_locked; --SDL_touch_locked;
SDL_UnlockMutex(SDL_touch_lock); SDL_UnlockMutex(SDL_event_lock);
} }
static void SDL_AssertTouchLocked(void) SDL_ASSERT_CAPABILITY(SDL_touch_lock) static void SDL_AssertTouchLocked(void) SDL_ASSERT_CAPABILITY(SDL_event_lock)
{ {
SDL_assert(SDL_touch_locked > 0); SDL_assert(SDL_touch_locked > 0);
} }
@ -614,9 +612,6 @@ void SDL_QuitTouch(void)
SDL_touchDevices = NULL; SDL_touchDevices = NULL;
} }
SDL_UnlockTouch(); SDL_UnlockTouch();
SDL_DestroyMutex(SDL_touch_lock);
SDL_touch_lock = NULL;
} }
int SDL_SendPinch(SDL_EventType type, Uint64 timestamp, SDL_Window *window, float scale) int SDL_SendPinch(SDL_EventType type, Uint64 timestamp, SDL_Window *window, float scale)

View file

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

View file

@ -114,19 +114,14 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#endif #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 int SDL_joysticks_locked;
static bool SDL_joysticks_initialized; static bool SDL_joysticks_initialized;
static bool SDL_joysticks_quitting; static bool SDL_joysticks_quitting;
static bool SDL_joystick_being_added; static bool SDL_joystick_being_added;
static SDL_Joystick *SDL_joysticks 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_joystick_lock) = 0; static int SDL_joystick_player_count SDL_GUARDED_BY(SDL_event_lock) = 0;
static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_joystick_lock) = NULL; static SDL_JoystickID *SDL_joystick_players SDL_GUARDED_BY(SDL_event_lock) = NULL;
static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_joystick_lock) = NULL; static SDL_HashTable *SDL_joystick_names SDL_GUARDED_BY(SDL_event_lock) = NULL;
static bool SDL_joystick_allows_background_events = false; static bool SDL_joystick_allows_background_events = false;
static Uint32 initial_old_xboxone_controllers[] = { static Uint32 initial_old_xboxone_controllers[] = {
@ -706,16 +701,13 @@ bool SDL_JoysticksQuitting(void)
void SDL_LockJoysticks(void) void SDL_LockJoysticks(void)
{ {
(void)SDL_AtomicIncRef(&SDL_joystick_lock_pending); SDL_LockMutex(SDL_event_lock);
SDL_LockMutex(SDL_joystick_lock);
(void)SDL_AtomicDecRef(&SDL_joystick_lock_pending);
++SDL_joysticks_locked; ++SDL_joysticks_locked;
} }
bool SDL_TryLockJoysticks(void) bool SDL_TryLockJoysticks(void)
{ {
if (SDL_TryLockMutex(SDL_joystick_lock)) { if (SDL_TryLockMutex(SDL_event_lock)) {
++SDL_joysticks_locked; ++SDL_joysticks_locked;
return true; return true;
} }
@ -724,34 +716,8 @@ bool SDL_TryLockJoysticks(void)
void SDL_UnlockJoysticks(void) void SDL_UnlockJoysticks(void)
{ {
bool last_unlock = false;
--SDL_joysticks_locked; --SDL_joysticks_locked;
SDL_UnlockMutex(SDL_event_lock);
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);
}
} }
bool SDL_JoysticksLocked(void) bool SDL_JoysticksLocked(void)
@ -892,11 +858,6 @@ bool SDL_InitJoysticks(void)
int i; int i;
bool result = false; bool result = false;
// Create the joystick list lock
if (SDL_joystick_lock == NULL) {
SDL_joystick_lock = SDL_CreateMutex();
}
if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) { if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
return false; return false;
} }

View file

@ -48,7 +48,7 @@ extern bool SDL_JoysticksQuitting(void);
extern bool SDL_JoysticksLocked(void); extern bool SDL_JoysticksLocked(void);
// Make sure we currently have the joysticks locked // 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 // Function to return whether there are any joysticks opened by the application
extern bool SDL_JoysticksOpened(void); extern bool SDL_JoysticksOpened(void);

View file

@ -33,11 +33,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
static char *SDL_steam_virtual_gamepad_info_file SDL_GUARDED_BY(SDL_joystick_lock) = NULL; 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_joystick_lock) = 0; 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_joystick_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_joystick_lock) = NULL; 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_joystick_lock) = 0; static int SDL_steam_virtual_gamepad_info_count SDL_GUARDED_BY(SDL_event_lock) = 0;
static Uint64 GetFileModificationTime(const char *file) static Uint64 GetFileModificationTime(const char *file)

View file

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

View file

@ -117,7 +117,7 @@ static int SDL_HIDAPI_numdrivers = 0;
static SDL_AtomicInt SDL_HIDAPI_updating_devices; static SDL_AtomicInt SDL_HIDAPI_updating_devices;
static bool SDL_HIDAPI_hints_changed = false; static bool SDL_HIDAPI_hints_changed = false;
static Uint32 SDL_HIDAPI_change_count = 0; 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 int SDL_HIDAPI_numjoysticks = 0;
static bool SDL_HIDAPI_combine_joycons = true; static bool SDL_HIDAPI_combine_joycons = true;
static bool initialized = false; static bool initialized = false;

View file

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

View file

@ -28,7 +28,7 @@
#include "../SDL_sysjoystick.h" #include "../SDL_sysjoystick.h"
#include "../SDL_joystick_c.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) static joystick_hwdata *VIRTUAL_HWDataForInstance(SDL_JoystickID instance_id)
{ {

View file

@ -51,14 +51,9 @@ static SDL_SensorDriver *SDL_sensor_drivers[] = {
#endif #endif
}; };
#ifndef SDL_THREAD_SAFETY_ANALYSIS
static
#endif
SDL_Mutex *SDL_sensor_lock = NULL; // This needs to support recursive locks
static SDL_AtomicInt SDL_sensor_lock_pending;
static int SDL_sensors_locked; static int SDL_sensors_locked;
static bool SDL_sensors_initialized; static bool SDL_sensors_initialized;
static SDL_Sensor *SDL_sensors SDL_GUARDED_BY(SDL_sensor_lock) = NULL; static SDL_Sensor *SDL_sensors SDL_GUARDED_BY(SDL_event_lock) = NULL;
#define CHECK_SENSOR_MAGIC(sensor, result) \ #define CHECK_SENSOR_MAGIC(sensor, result) \
CHECK_PARAM(!SDL_ObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR)) { \ CHECK_PARAM(!SDL_ObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR)) { \
@ -74,43 +69,14 @@ bool SDL_SensorsInitialized(void)
void SDL_LockSensors(void) void SDL_LockSensors(void)
{ {
(void)SDL_AtomicIncRef(&SDL_sensor_lock_pending); SDL_LockMutex(SDL_event_lock);
SDL_LockMutex(SDL_sensor_lock);
(void)SDL_AtomicDecRef(&SDL_sensor_lock_pending);
++SDL_sensors_locked; ++SDL_sensors_locked;
} }
void SDL_UnlockSensors(void) void SDL_UnlockSensors(void)
{ {
bool last_unlock = false;
--SDL_sensors_locked; --SDL_sensors_locked;
SDL_UnlockMutex(SDL_event_lock);
if (!SDL_sensors_initialized) {
// NOTE: There's a small window here where another thread could lock the mutex after we've checked for pending locks
if (!SDL_sensors_locked && SDL_GetAtomicInt(&SDL_sensor_lock_pending) == 0) {
last_unlock = true;
}
}
/* The last unlock after sensors are uninitialized will cleanup the mutex,
* allowing applications to lock sensors while reinitializing the system.
*/
if (last_unlock) {
SDL_Mutex *sensor_lock = SDL_sensor_lock;
SDL_LockMutex(sensor_lock);
{
SDL_UnlockMutex(SDL_sensor_lock);
SDL_sensor_lock = NULL;
}
SDL_UnlockMutex(sensor_lock);
SDL_DestroyMutex(sensor_lock);
} else {
SDL_UnlockMutex(SDL_sensor_lock);
}
} }
bool SDL_SensorsLocked(void) bool SDL_SensorsLocked(void)
@ -128,11 +94,6 @@ bool SDL_InitSensors(void)
int i; int i;
bool status; bool status;
// Create the sensor list lock
if (SDL_sensor_lock == NULL) {
SDL_sensor_lock = SDL_CreateMutex();
}
if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) { if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
return false; return false;
} }

View file

@ -23,10 +23,6 @@
#ifndef SDL_sensor_c_h_ #ifndef SDL_sensor_c_h_
#define SDL_sensor_c_h_ #define SDL_sensor_c_h_
#ifdef SDL_THREAD_SAFETY_ANALYSIS
extern SDL_Mutex *SDL_sensor_lock;
#endif
struct SDL_SensorDriver; struct SDL_SensorDriver;
// Useful functions and variables from SDL_sensor.c // Useful functions and variables from SDL_sensor.c
@ -42,10 +38,10 @@ extern bool SDL_SensorsInitialized(void);
extern bool SDL_SensorsLocked(void); extern bool SDL_SensorsLocked(void);
// Make sure we currently have the sensors locked // Make sure we currently have the sensors locked
extern void SDL_AssertSensorsLocked(void) SDL_ASSERT_CAPABILITY(SDL_sensor_lock); extern void SDL_AssertSensorsLocked(void) SDL_ASSERT_CAPABILITY(SDL_event_lock);
extern void SDL_LockSensors(void) SDL_ACQUIRE(SDL_sensor_lock); extern void SDL_LockSensors(void) SDL_ACQUIRE(SDL_event_lock);
extern void SDL_UnlockSensors(void) SDL_RELEASE(SDL_sensor_lock); extern void SDL_UnlockSensors(void) SDL_RELEASE(SDL_event_lock);
// Function to return whether there are any sensors opened by the application // Function to return whether there are any sensors opened by the application
extern bool SDL_SensorsOpened(void); extern bool SDL_SensorsOpened(void);

View file

@ -26,8 +26,9 @@
// This is the system specific header for the SDL sensor API // This is the system specific header for the SDL sensor API
#include "SDL_sensor_c.h" #include "SDL_sensor_c.h"
#include "../events/SDL_events_c.h"
#define _guarded SDL_GUARDED_BY(SDL_sensor_lock) #define _guarded SDL_GUARDED_BY(SDL_event_lock)
// The SDL sensor structure // The SDL sensor structure
struct SDL_Sensor struct SDL_Sensor