|
|
@@ -739,6 +739,21 @@ SDL_AudioDevice *SDL_AddAudioDevice(bool recording, const char *name, const SDL_
|
|
|
return device;
|
|
|
}
|
|
|
|
|
|
+// you must hold the device lock when calling this!
|
|
|
+static void SetAudioDeviceZombieFunctions(SDL_AudioDevice *device)
|
|
|
+{
|
|
|
+ // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep
|
|
|
+ // making progress until the app closes it. Otherwise, streams might continue to
|
|
|
+ // accumulate waste data that never drains, apps that depend on audio callbacks to
|
|
|
+ // progress will freeze, etc.
|
|
|
+ device->WaitDevice = ZombieWaitDevice;
|
|
|
+ device->GetDeviceBuf = ZombieGetDeviceBuf;
|
|
|
+ device->PlayDevice = ZombiePlayDevice;
|
|
|
+ device->WaitRecordingDevice = ZombieWaitDevice;
|
|
|
+ device->RecordDevice = ZombieRecordDevice;
|
|
|
+ device->FlushRecording = ZombieFlushRecording;
|
|
|
+}
|
|
|
+
|
|
|
// Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the audio device's thread.
|
|
|
static void SDLCALL SDL_AudioDeviceDisconnected_OnMainThread(void *userdata)
|
|
|
{
|
|
|
@@ -760,18 +775,10 @@ static void SDLCALL SDL_AudioDeviceDisconnected_OnMainThread(void *userdata)
|
|
|
const bool is_default_device = ((devid == current_audio.default_playback_device_id) || (devid == current_audio.default_recording_device_id));
|
|
|
SDL_UnlockRWLock(current_audio.subsystem_rwlock);
|
|
|
|
|
|
- const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 1);
|
|
|
+ // zombie==2 means "we've handled the disconnect events". 1=="we marked this as dead from a random thread but haven't done anything else" 0==we think we're still alive.
|
|
|
+ const bool first_disconnect = SDL_CompareAndSwapAtomicInt(&device->zombie, 0, 2) || SDL_CompareAndSwapAtomicInt(&device->zombie, 1, 2);
|
|
|
if (first_disconnect) { // if already disconnected this device, don't do it twice.
|
|
|
- // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep
|
|
|
- // making progress until the app closes it. Otherwise, streams might continue to
|
|
|
- // accumulate waste data that never drains, apps that depend on audio callbacks to
|
|
|
- // progress will freeze, etc.
|
|
|
- device->WaitDevice = ZombieWaitDevice;
|
|
|
- device->GetDeviceBuf = ZombieGetDeviceBuf;
|
|
|
- device->PlayDevice = ZombiePlayDevice;
|
|
|
- device->WaitRecordingDevice = ZombieWaitDevice;
|
|
|
- device->RecordDevice = ZombieRecordDevice;
|
|
|
- device->FlushRecording = ZombieFlushRecording;
|
|
|
+ SetAudioDeviceZombieFunctions(device); // in case we beat the device thread to this.
|
|
|
|
|
|
// on default devices, dump any logical devices that explicitly opened this device. Things that opened the system default can stay.
|
|
|
// on non-default devices, dump everything.
|
|
|
@@ -822,12 +829,15 @@ static void SDLCALL SDL_AudioDeviceDisconnected_OnMainThread(void *userdata)
|
|
|
|
|
|
void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
|
|
|
{
|
|
|
+ //SDL_Log("AUDIO DEVICE DISCONNECTED %p '%s'", device, device ? device->name : NULL);
|
|
|
+
|
|
|
// 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_CompareAndSwapAtomicInt(&device->zombie, 0, 1); // note that we're (un)dead right now, if we haven't already, but leave the event notifications for the main thread.
|
|
|
SDL_RunOnMainThread(SDL_AudioDeviceDisconnected_OnMainThread, device, false);
|
|
|
}
|
|
|
}
|
|
|
@@ -1183,6 +1193,11 @@ bool SDL_PlaybackAudioThreadIterate(SDL_AudioDevice *device)
|
|
|
return false; // we're done, shut it down.
|
|
|
}
|
|
|
|
|
|
+ if (SDL_GetAtomicInt(&device->zombie) == 1) {
|
|
|
+ // we've been marked as (un)dead but not fully processed. Set up the zombie functions so we stop talking to the real backend.
|
|
|
+ SetAudioDeviceZombieFunctions(device);
|
|
|
+ }
|
|
|
+
|
|
|
bool failed = false;
|
|
|
int buffer_size = device->buffer_size;
|
|
|
Uint8 *device_buffer = device->GetDeviceBuf(device, &buffer_size);
|
|
|
@@ -1349,6 +1364,11 @@ bool SDL_RecordingAudioThreadIterate(SDL_AudioDevice *device)
|
|
|
return false; // we're done, shut it down.
|
|
|
}
|
|
|
|
|
|
+ if (SDL_GetAtomicInt(&device->zombie) == 1) {
|
|
|
+ // we've been marked as (un)dead but not fully processed. Set up the zombie functions so we stop talking to the real backend.
|
|
|
+ SetAudioDeviceZombieFunctions(device);
|
|
|
+ }
|
|
|
+
|
|
|
bool failed = false;
|
|
|
|
|
|
if (!device->logical_devices) {
|