Browse Source

Make sure joysticks are locked when adding and removing them

Fixes https://github.com/libsdl-org/SDL/issues/8146
Sam Lantinga 1 year ago
parent
commit
ed1e0c1530
1 changed files with 58 additions and 32 deletions
  1. 58 32
      src/joystick/linux/SDL_sysjoystick.c

+ 58 - 32
src/joystick/linux/SDL_sysjoystick.c

@@ -145,8 +145,8 @@ typedef enum
 static EnumerationMethod enumeration_method = ENUMERATION_UNSET;
 
 static SDL_bool IsJoystickJSNode(const char *node);
-static int MaybeAddDevice(const char *path);
-static int MaybeRemoveDevice(const char *path);
+static void MaybeAddDevice(const char *path);
+static void MaybeRemoveDevice(const char *path);
 
 /* A linked list of available joysticks */
 typedef struct SDL_joylist_item
@@ -176,10 +176,10 @@ typedef struct SDL_sensorlist_item
 } SDL_sensorlist_item;
 
 static SDL_bool SDL_classic_joysticks = SDL_FALSE;
-static SDL_joylist_item *SDL_joylist = NULL;
-static SDL_joylist_item *SDL_joylist_tail = NULL;
-static int numjoysticks = 0;
-static SDL_sensorlist_item *SDL_sensorlist = NULL;
+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 int inotify_fd = -1;
 
 static Uint64 last_joy_detect_time;
@@ -384,7 +384,7 @@ static void FreeSensorlistItem(SDL_sensorlist_item *item)
     SDL_free(item);
 }
 
-static int MaybeAddDevice(const char *path)
+static void MaybeAddDevice(const char *path)
 {
     struct stat sb;
     int fd = -1;
@@ -394,28 +394,30 @@ static int MaybeAddDevice(const char *path)
     SDL_sensorlist_item *item_sensor;
 
     if (path == NULL) {
-        return -1;
+        return;
     }
 
     if (stat(path, &sb) == -1) {
-        return -1;
+        return;
     }
 
+    SDL_LockJoysticks();
+
     /* Check to make sure it's not already in list. */
     for (item = SDL_joylist; item != NULL; item = item->next) {
         if (sb.st_rdev == item->devnum) {
-            return -1; /* already have this one */
+            goto done; /* already have this one */
         }
     }
     for (item_sensor = SDL_sensorlist; item_sensor != NULL; item_sensor = item_sensor->next) {
         if (sb.st_rdev == item_sensor->devnum) {
-            return -1; /* already have this one */
+            goto done; /* already have this one */
         }
     }
 
     fd = open(path, O_RDONLY | O_CLOEXEC, 0);
     if (fd < 0) {
-        return -1;
+        goto done;
     }
 
 #ifdef DEBUG_INPUT_EVENTS
@@ -430,7 +432,7 @@ static int MaybeAddDevice(const char *path)
         item = (SDL_joylist_item *)SDL_calloc(1, sizeof(SDL_joylist_item));
         if (item == NULL) {
             SDL_free(name);
-            return -1;
+            goto done;
         }
 
         item->devnum = sb.st_rdev;
@@ -440,7 +442,7 @@ static int MaybeAddDevice(const char *path)
 
         if ((item->path == NULL) || (item->name == NULL)) {
             FreeJoylistItem(item);
-            return -1;
+            goto done;
         }
 
         item->device_instance = SDL_GetNextObjectID();
@@ -455,7 +457,7 @@ static int MaybeAddDevice(const char *path)
         ++numjoysticks;
 
         SDL_PrivateJoystickAdded(item->device_instance);
-        return numjoysticks;
+        goto done;
     }
 
     if (IsSensor(path, fd)) {
@@ -465,27 +467,30 @@ static int MaybeAddDevice(const char *path)
         close(fd);
         item_sensor = (SDL_sensorlist_item *)SDL_calloc(1, sizeof(SDL_sensorlist_item));
         if (item_sensor == NULL) {
-            return -1;
+            goto done;
         }
         item_sensor->devnum = sb.st_rdev;
         item_sensor->path = SDL_strdup(path);
 
         if (item_sensor->path == NULL) {
             FreeSensorlistItem(item_sensor);
-            return -1;
+            goto done;
         }
 
         item_sensor->next = SDL_sensorlist;
         SDL_sensorlist = item_sensor;
-        return -1;
+        goto done;
     }
 
     close(fd);
-    return -1;
+done:
+    SDL_UnlockJoysticks();
 }
 
 static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)
 {
+    SDL_AssertJoysticksLocked();
+
     if (item->hwdata) {
         item->hwdata->item = NULL;
     }
@@ -510,6 +515,8 @@ static void RemoveJoylistItem(SDL_joylist_item *item, SDL_joylist_item *prev)
 
 static void RemoveSensorlistItem(SDL_sensorlist_item *item, SDL_sensorlist_item *prev)
 {
+    SDL_AssertJoysticksLocked();
+
     if (item->hwdata) {
         item->hwdata->item_sensor = NULL;
     }
@@ -526,7 +533,7 @@ static void RemoveSensorlistItem(SDL_sensorlist_item *item, SDL_sensorlist_item
     FreeSensorlistItem(item);
 }
 
-static int MaybeRemoveDevice(const char *path)
+static void MaybeRemoveDevice(const char *path)
 {
     SDL_joylist_item *item;
     SDL_joylist_item *prev = NULL;
@@ -534,15 +541,15 @@ static int MaybeRemoveDevice(const char *path)
     SDL_sensorlist_item *prev_sensor = NULL;
 
     if (path == NULL) {
-        return -1;
+        return;
     }
 
+    SDL_LockJoysticks();
     for (item = SDL_joylist; item != NULL; item = item->next) {
         /* found it, remove it. */
         if (SDL_strcmp(path, item->path) == 0) {
-            const int retval = item->device_instance;
             RemoveJoylistItem(item, prev);
-            return retval;
+            goto done;
         }
         prev = item;
     }
@@ -550,21 +557,24 @@ static int MaybeRemoveDevice(const char *path)
         /* found it, remove it. */
         if (SDL_strcmp(path, item_sensor->path) == 0) {
             RemoveSensorlistItem(item_sensor, prev_sensor);
-            return -1;
+            goto done;
         }
         prev_sensor = item_sensor;
     }
-
-    return -1;
+done:
+    SDL_UnlockJoysticks();
 }
 
 static void HandlePendingRemovals(void)
 {
     SDL_joylist_item *prev = NULL;
-    SDL_joylist_item *item = SDL_joylist;
+    SDL_joylist_item *item = NULL;
     SDL_sensorlist_item *prev_sensor = NULL;
-    SDL_sensorlist_item *item_sensor = SDL_sensorlist;
+    SDL_sensorlist_item *item_sensor = NULL;
+
+    SDL_AssertJoysticksLocked();
 
+    item = SDL_joylist;
     while (item != NULL) {
         if (item->hwdata && item->hwdata->gone) {
             RemoveJoylistItem(item, prev);
@@ -580,6 +590,7 @@ static void HandlePendingRemovals(void)
         }
     }
 
+    item_sensor = SDL_sensorlist;
     while (item_sensor != NULL) {
         if (item_sensor->hwdata && item_sensor->hwdata->sensor_gone) {
             RemoveSensorlistItem(item_sensor, prev_sensor);
@@ -616,6 +627,7 @@ static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickG
     }
 
     *device_instance = item->device_instance = SDL_GetNextObjectID();
+    SDL_LockJoysticks();
     if (SDL_joylist_tail == NULL) {
         SDL_joylist = SDL_joylist_tail = item;
     } else {
@@ -627,6 +639,7 @@ static SDL_bool SteamControllerConnectedCallback(const char *name, SDL_JoystickG
     ++numjoysticks;
 
     SDL_PrivateJoystickAdded(item->device_instance);
+    SDL_UnlockJoysticks();
 
     return SDL_TRUE;
 }
@@ -636,14 +649,16 @@ static void SteamControllerDisconnectedCallback(SDL_JoystickID device_instance)
     SDL_joylist_item *item;
     SDL_joylist_item *prev = NULL;
 
+    SDL_LockJoysticks();
     for (item = SDL_joylist; item != NULL; item = item->next) {
         /* found it, remove it. */
         if (item->device_instance == device_instance) {
             RemoveJoylistItem(item, prev);
-            return;
+            break;
         }
         prev = item;
     }
+    SDL_UnlockJoysticks();
 }
 
 static int StrHasPrefix(const char *string, const char *prefix)
@@ -969,17 +984,22 @@ static int LINUX_JoystickInit(void)
 
 static int LINUX_JoystickGetCount(void)
 {
+    SDL_AssertJoysticksLocked();
+
     return numjoysticks;
 }
 
 static SDL_joylist_item *GetJoystickByDevIndex(int device_index)
 {
-    SDL_joylist_item *item = SDL_joylist;
+    SDL_joylist_item *item;
+
+    SDL_AssertJoysticksLocked();
 
     if ((device_index < 0) || (device_index >= numjoysticks)) {
         return NULL;
     }
 
+    item = SDL_joylist;
     while (device_index > 0) {
         SDL_assert(item != NULL);
         device_index--;
@@ -1382,6 +1402,8 @@ static SDL_sensorlist_item *GetSensor(SDL_joylist_item *item)
     char uniq_item[128];
     int fd_item = -1;
 
+    SDL_AssertJoysticksLocked();
+
     if (item == NULL || SDL_sensorlist == NULL) {
         return NULL;
     }
@@ -1435,11 +1457,12 @@ static SDL_sensorlist_item *GetSensor(SDL_joylist_item *item)
  */
 static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
 {
-    SDL_joylist_item *item = GetJoystickByDevIndex(device_index);
-    SDL_sensorlist_item *item_sensor = GetSensor(item);
+    SDL_joylist_item *item;
+    SDL_sensorlist_item *item_sensor;
 
     SDL_AssertJoysticksLocked();
 
+    item = GetJoystickByDevIndex(device_index);
     if (item == NULL) {
         return SDL_SetError("No such device");
     }
@@ -1451,6 +1474,7 @@ static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
         return SDL_OutOfMemory();
     }
 
+    item_sensor = GetSensor(item);
     if (PrepareJoystickHwdata(joystick, item, item_sensor) == -1) {
         SDL_free(joystick->hwdata);
         joystick->hwdata = NULL;
@@ -2051,6 +2075,8 @@ static void LINUX_JoystickQuit(void)
     SDL_sensorlist_item *item_sensor = NULL;
     SDL_sensorlist_item *next_sensor = NULL;
 
+    SDL_AssertJoysticksLocked();
+
     if (inotify_fd >= 0) {
         close(inotify_fd);
         inotify_fd = -1;