mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-05 09:26:25 +00:00
audio: Handle device disconnects on the main thread.
This avoids situations like: - PulseAudio holds its own lock in the hotplug thread. - The hotplug thread notices a device went away. - The hotplug thread calls SDL_AudioDeviceDisconnected(). - SDL_AudioDeviceDisconnected() tries to grab the device lock. - The device thread is holding the device lock... - ...but is currently waiting on PulseAudio's lock to release. In short: deadlock. It's better to queue this work to run on the main thread, where we can guarantee a start with _none_ of the audio subsystem locks held.
This commit is contained in:
@@ -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.
|
// 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) {
|
SDL_AudioDevice *device = (SDL_AudioDevice *) userdata;
|
||||||
return;
|
SDL_assert(device != NULL);
|
||||||
}
|
|
||||||
|
|
||||||
// Save off removal info in a list so we can send events for each, next
|
// 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
|
// 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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user