From 71f4af7322a09de98c1417c1a8e0fc265a4b922e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 9 Feb 2026 21:17:24 -0800
Subject: [PATCH] Eliminate contention between HIDAPI controller reads and
writes
Rumble can often take a long time, and it is theoretically safe to simultaneously read and write hidapi devices on all platforms.
Fixes https://github.com/libsdl-org/SDL/issues/9441
---
src/joystick/hidapi/SDL_hidapi_rumble.c | 2 -
src/joystick/hidapi/SDL_hidapi_switch.c | 10 -----
src/joystick/hidapi/SDL_hidapijoystick.c | 44 +++++-----------------
src/joystick/hidapi/SDL_hidapijoystick_c.h | 4 --
4 files changed, 10 insertions(+), 50 deletions(-)
diff --git a/src/joystick/hidapi/SDL_hidapi_rumble.c b/src/joystick/hidapi/SDL_hidapi_rumble.c
index ef3fede523163..7747b9dd4bb95 100644
--- a/src/joystick/hidapi/SDL_hidapi_rumble.c
+++ b/src/joystick/hidapi/SDL_hidapi_rumble.c
@@ -77,14 +77,12 @@ static int SDLCALL SDL_HIDAPI_RumbleThread(void *data)
SDL_UnlockMutex(SDL_HIDAPI_rumble_lock);
if (request) {
- SDL_LockMutex(request->device->dev_lock);
if (request->device->dev) {
#ifdef DEBUG_RUMBLE
HIDAPI_DumpPacket("Rumble packet: size = %d", request->data, request->size);
#endif
SDL_hid_write(request->device->dev, request->data, request->size);
}
- SDL_UnlockMutex(request->device->dev_lock);
if (request->callback) {
request->callback(request->userdata);
}
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index 514e158889f0e..c96c7842e23c7 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -2726,17 +2726,7 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C
Uint64 now = SDL_GetTicks();
if (now >= (ctx->m_ulLastIMUReset + IMU_RESET_DELAY_MS)) {
- SDL_HIDAPI_Device *device = ctx->device;
-
- if (device->updating) {
- SDL_UnlockMutex(device->dev_lock);
- }
-
SetIMUEnabled(ctx, true);
-
- if (device->updating) {
- SDL_LockMutex(device->dev_lock);
- }
ctx->m_ulLastIMUReset = now;
}
diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c
index f45c5142a3f85..5ad2c9057ab23 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick.c
+++ b/src/joystick/hidapi/SDL_hidapijoystick.c
@@ -72,7 +72,7 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
#endif
#ifdef SDL_JOYSTICK_HIDAPI_STEAMDECK
&SDL_HIDAPI_DriverSteamTriton,
-#endif
+#endif
#ifdef SDL_JOYSTICK_HIDAPI_SWITCH
&SDL_HIDAPI_DriverNintendoClassic,
&SDL_HIDAPI_DriverJoyCons,
@@ -439,19 +439,15 @@ static void HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
device->driver->FreeDevice(device);
device->driver = NULL;
- SDL_LockMutex(device->dev_lock);
- {
- if (device->dev) {
- SDL_hid_close(device->dev);
- device->dev = NULL;
- }
+ if (device->dev) {
+ SDL_hid_close(device->dev);
+ device->dev = NULL;
+ }
- if (device->context) {
- SDL_free(device->context);
- device->context = NULL;
- }
+ if (device->context) {
+ SDL_free(device->context);
+ device->context = NULL;
}
- SDL_UnlockMutex(device->dev_lock);
}
static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, bool *removed) SDL_NO_THREAD_SAFETY_ANALYSIS // We unlock the joystick lock to be able to open the HID device on Android
@@ -916,7 +912,6 @@ static SDL_HIDAPI_Device *HIDAPI_AddDevice(const struct SDL_hid_device_info *inf
device->usage_page = info->usage_page;
device->usage = info->usage;
device->is_bluetooth = (info->bus_type == SDL_HID_API_BUS_BLUETOOTH);
- device->dev_lock = SDL_CreateMutex();
// Need the device name before getting the driver to know whether to ignore this device
{
@@ -1013,7 +1008,6 @@ static void HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
}
SDL_SetObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_JOYSTICK, false);
- SDL_DestroyMutex(device->dev_lock);
SDL_free(device->manufacturer_string);
SDL_free(device->product_string);
SDL_free(device->serial);
@@ -1434,12 +1428,7 @@ void HIDAPI_UpdateDevices(void)
continue;
}
if (device->driver) {
- if (SDL_TryLockMutex(device->dev_lock)) {
- device->updating = true;
- device->driver->UpdateDevice(device);
- device->updating = false;
- SDL_UnlockMutex(device->dev_lock);
- }
+ device->driver->UpdateDevice(device);
}
}
HIDAPI_FinishUpdatingDevices();
@@ -1552,11 +1541,7 @@ static bool HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
hwdata->device = device;
// Process any pending reports before opening the device
- SDL_LockMutex(device->dev_lock);
- device->updating = true;
device->driver->UpdateDevice(device);
- device->updating = false;
- SDL_UnlockMutex(device->dev_lock);
// UpdateDevice() may have called HIDAPI_JoystickDisconnected() if the device went away
if (device->num_joysticks == 0) {
@@ -1682,22 +1667,13 @@ static void HIDAPI_JoystickClose(SDL_Joystick *joystick) SDL_NO_THREAD_SAFETY_AN
if (joystick->hwdata) {
SDL_HIDAPI_Device *device = joystick->hwdata->device;
- int i;
// Wait up to 30 ms for pending rumble to complete
- if (device->updating) {
- // Unlock the device so rumble can complete
- SDL_UnlockMutex(device->dev_lock);
- }
- for (i = 0; i < 3; ++i) {
+ for (int i = 0; i < 3; ++i) {
if (SDL_GetAtomicInt(&device->rumble_pending) > 0) {
SDL_Delay(10);
}
}
- if (device->updating) {
- // Relock the device
- SDL_LockMutex(device->dev_lock);
- }
device->driver->CloseJoystick(device, joystick);
diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h
index cd3f50a03d673..59c62f6fe5ba5 100644
--- a/src/joystick/hidapi/SDL_hidapijoystick_c.h
+++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h
@@ -100,7 +100,6 @@ typedef struct SDL_HIDAPI_Device
struct SDL_HIDAPI_DeviceDriver *driver;
void *context;
- SDL_Mutex *dev_lock;
SDL_hid_device *dev;
SDL_AtomicInt rumble_pending;
int num_joysticks;
@@ -109,9 +108,6 @@ typedef struct SDL_HIDAPI_Device
// Used during scanning for device changes
bool seen;
- // Used to flag that the device is being updated
- bool updating;
-
// Used to flag devices that failed open
// This can happen on Windows with Bluetooth devices that have turned off
bool broken;