SDL: audio open: ensure 2 devices don't get the same id

From 484d5fd6cfaf7abdd6d20b9838bbdac8c97bba2d Mon Sep 17 00:00:00 2001
From: Nicolas Cian <[EMAIL REDACTED]>
Date: Wed, 5 Oct 2022 13:19:38 +0200
Subject: [PATCH] audio open: ensure 2 devices don't get the same id

---
 src/audio/SDL_audio.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index e2f7412c994e..e33e41dbc33e 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -1313,7 +1313,7 @@ open_audio_device(const char *devname, int iscapture,
         return 0;
     }
 
-    /* !!! FIXME: there is a race condition here if two devices open from two threads at once. */
+    SDL_LockMutex(current_audio.detectionLock);
     /* Find an available device ID... */
     for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
         if (open_devices[id] == NULL) {
@@ -1323,6 +1323,7 @@ open_audio_device(const char *devname, int iscapture,
 
     if (id == SDL_arraysize(open_devices)) {
         SDL_SetError("Too many open audio devices");
+        SDL_UnlockMutex(current_audio.detectionLock);
         return 0;
     }
 
@@ -1330,6 +1331,7 @@ open_audio_device(const char *devname, int iscapture,
         obtained = &_obtained;
     }
     if (!prepare_audiospec(desired, obtained)) {
+        SDL_UnlockMutex(current_audio.detectionLock);
         return 0;
     }
 
@@ -1351,6 +1353,7 @@ open_audio_device(const char *devname, int iscapture,
     if ((iscapture) && (current_audio.impl.OnlyHasDefaultCaptureDevice)) {
         if ((devname) && (SDL_strcmp(devname, DEFAULT_INPUT_DEVNAME) != 0)) {
             SDL_SetError("No such device");
+            SDL_UnlockMutex(current_audio.detectionLock);
             return 0;
         }
         devname = NULL;
@@ -1358,11 +1361,13 @@ open_audio_device(const char *devname, int iscapture,
         for (i = 0; i < SDL_arraysize(open_devices); i++) {
             if ((open_devices[i]) && (open_devices[i]->iscapture)) {
                 SDL_SetError("Audio device already open");
+                SDL_UnlockMutex(current_audio.detectionLock);
                 return 0;
             }
         }
     } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
         if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
+            SDL_UnlockMutex(current_audio.detectionLock);
             SDL_SetError("No such device");
             return 0;
         }
@@ -1370,6 +1375,7 @@ open_audio_device(const char *devname, int iscapture,
 
         for (i = 0; i < SDL_arraysize(open_devices); i++) {
             if ((open_devices[i]) && (!open_devices[i]->iscapture)) {
+                SDL_UnlockMutex(current_audio.detectionLock);
                 SDL_SetError("Audio device already open");
                 return 0;
             }
@@ -1382,20 +1388,19 @@ open_audio_device(const char *devname, int iscapture,
            It might still need to open a device based on the string for,
            say, a network audio server, but this optimizes some cases. */
         SDL_AudioDeviceItem *item;
-        SDL_LockMutex(current_audio.detectionLock);
         for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
             if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
                 handle = item->handle;
                 break;
             }
         }
-        SDL_UnlockMutex(current_audio.detectionLock);
     }
 
     if (!current_audio.impl.AllowsArbitraryDeviceNames) {
         /* has to be in our device list, or the default device. */
         if ((handle == NULL) && (devname != NULL)) {
             SDL_SetError("No such device.");
+            SDL_UnlockMutex(current_audio.detectionLock);
             return 0;
         }
     }
@@ -1403,6 +1408,7 @@ open_audio_device(const char *devname, int iscapture,
     device = (SDL_AudioDevice *) SDL_calloc(1, sizeof (SDL_AudioDevice));
     if (device == NULL) {
         SDL_OutOfMemory();
+        SDL_UnlockMutex(current_audio.detectionLock);
         return 0;
     }
     device->id = id + 1;
@@ -1419,6 +1425,7 @@ open_audio_device(const char *devname, int iscapture,
         device->mixer_lock = SDL_CreateMutex();
         if (device->mixer_lock == NULL) {
             close_audio_device(device);
+            SDL_UnlockMutex(current_audio.detectionLock);
             SDL_SetError("Couldn't create mixer lock");
             return 0;
         }
@@ -1433,6 +1440,7 @@ open_audio_device(const char *devname, int iscapture,
 
     if (current_audio.impl.OpenDevice(device, devname) < 0) {
         close_audio_device(device);
+        SDL_UnlockMutex(current_audio.detectionLock);
         return 0;
     }
 
@@ -1488,6 +1496,7 @@ open_audio_device(const char *devname, int iscapture,
 
         if (!device->stream) {
             close_audio_device(device);
+            SDL_UnlockMutex(current_audio.detectionLock);
             return 0;
         }
     }
@@ -1497,6 +1506,7 @@ open_audio_device(const char *devname, int iscapture,
         device->buffer_queue = SDL_NewDataQueue(SDL_AUDIOBUFFERQUEUE_PACKETLEN, obtained->size * 2);
         if (!device->buffer_queue) {
             close_audio_device(device);
+            SDL_UnlockMutex(current_audio.detectionLock);
             SDL_SetError("Couldn't create audio buffer queue");
             return 0;
         }
@@ -1514,6 +1524,7 @@ open_audio_device(const char *devname, int iscapture,
     device->work_buffer = (Uint8 *) SDL_malloc(device->work_buffer_len);
     if (device->work_buffer == NULL) {
         close_audio_device(device);
+        SDL_UnlockMutex(current_audio.detectionLock);
         SDL_OutOfMemory();
         return 0;
     }
@@ -1534,9 +1545,11 @@ open_audio_device(const char *devname, int iscapture,
         if (device->thread == NULL) {
             close_audio_device(device);
             SDL_SetError("Couldn't create audio thread");
+            SDL_UnlockMutex(current_audio.detectionLock);
             return 0;
         }
     }
+    SDL_UnlockMutex(current_audio.detectionLock);
 
     return device->id;
 }