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

@@ -51,14 +51,9 @@ static SDL_SensorDriver *SDL_sensor_drivers[] = {
#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 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) \
CHECK_PARAM(!SDL_ObjectValid(sensor, SDL_OBJECT_TYPE_SENSOR)) { \
@@ -74,43 +69,14 @@ bool SDL_SensorsInitialized(void)
void SDL_LockSensors(void)
{
(void)SDL_AtomicIncRef(&SDL_sensor_lock_pending);
SDL_LockMutex(SDL_sensor_lock);
(void)SDL_AtomicDecRef(&SDL_sensor_lock_pending);
SDL_LockMutex(SDL_event_lock);
++SDL_sensors_locked;
}
void SDL_UnlockSensors(void)
{
bool last_unlock = false;
--SDL_sensors_locked;
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);
}
SDL_UnlockMutex(SDL_event_lock);
}
bool SDL_SensorsLocked(void)
@@ -128,11 +94,6 @@ bool SDL_InitSensors(void)
int i;
bool status;
// Create the sensor list lock
if (SDL_sensor_lock == NULL) {
SDL_sensor_lock = SDL_CreateMutex();
}
if (!SDL_InitSubSystem(SDL_INIT_EVENTS)) {
return false;
}

View File

@@ -23,10 +23,6 @@
#ifndef SDL_sensor_c_h_
#define SDL_sensor_c_h_
#ifdef SDL_THREAD_SAFETY_ANALYSIS
extern SDL_Mutex *SDL_sensor_lock;
#endif
struct SDL_SensorDriver;
// Useful functions and variables from SDL_sensor.c
@@ -42,10 +38,10 @@ extern bool SDL_SensorsInitialized(void);
extern bool SDL_SensorsLocked(void);
// 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_UnlockSensors(void) SDL_RELEASE(SDL_sensor_lock);
extern void SDL_LockSensors(void) SDL_ACQUIRE(SDL_event_lock);
extern void SDL_UnlockSensors(void) SDL_RELEASE(SDL_event_lock);
// Function to return whether there are any sensors opened by the application
extern bool SDL_SensorsOpened(void);

View File

@@ -26,8 +26,9 @@
// This is the system specific header for the SDL sensor API
#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
struct SDL_Sensor