diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index ae8b955ecf..ba6e2c21d2 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -734,11 +734,10 @@ SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_ } // Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the audio device's thread. -void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) +static void SDLCALL SDL_AudioDeviceDisconnected_OnMainThread(void *userdata) { - if (!device) { - return; - } + SDL_AudioDevice *device = (SDL_AudioDevice *) userdata; + SDL_assert(device != NULL); // Save off removal info in a list so we can send events for each, next // time the event queue pumps, in case something tries to close a device @@ -808,6 +807,23 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) UnrefPhysicalAudioDevice(device); } + + // We always ref this in SDL_AudioDeviceDisconnected(), so if multiple attempts + // to disconnect are queued, the pointer stays valid until the last one comes + // through. + UnrefPhysicalAudioDevice(device); +} + +void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device) +{ + // lots of risk of various audio backends deadlocking because they're calling + // this while holding a backend-specific lock, which causes problems when we + // want to obtain the device lock while its audio thread is also waiting for + // that lock to be released. So just queue the work on the main thread. + if (device) { + RefPhysicalAudioDevice(device); + SDL_RunOnMainThread(SDL_AudioDeviceDisconnected_OnMainThread, device, false); + } }