mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-10-16 14:56:00 +00:00
audio: Deal with race conditions against default device changes.
This catches the case where we obtain a logical device while the default is changing in another thread, so you accidentally end up with the previous default physical device locked and returned from ObtainLogicalAudioDevice.
This commit is contained in:
@@ -306,17 +306,17 @@ static void ReleaseAudioDevice(SDL_AudioDevice *device) SDL_NO_THREAD_SAFETY_ANA
|
||||
}
|
||||
|
||||
// If found, this locks _the physical device_ this logical device is associated with, before returning.
|
||||
static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_AudioDevice **device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_ACQUIRE
|
||||
static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid, SDL_AudioDevice **_device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_ACQUIRE
|
||||
{
|
||||
SDL_assert(device != NULL);
|
||||
|
||||
*device = NULL;
|
||||
SDL_assert(_device != NULL);
|
||||
|
||||
if (!SDL_GetCurrentAudioDriver()) {
|
||||
SDL_SetError("Audio subsystem is not initialized");
|
||||
*_device = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_AudioDevice *device = NULL;
|
||||
SDL_LogicalAudioDevice *logdev = NULL;
|
||||
|
||||
// bit #1 of devid is set for physical devices and unset for logical.
|
||||
@@ -325,19 +325,36 @@ static SDL_LogicalAudioDevice *ObtainLogicalAudioDevice(SDL_AudioDeviceID devid,
|
||||
SDL_LockRWLockForReading(current_audio.device_hash_lock);
|
||||
SDL_FindInHashTable(current_audio.device_hash, (const void *) (uintptr_t) devid, (const void **) &logdev);
|
||||
if (logdev) {
|
||||
*device = logdev->physical_device;
|
||||
RefPhysicalAudioDevice(*device); // reference it, in case the logical device migrates to a new default.
|
||||
device = logdev->physical_device;
|
||||
SDL_assert(device != NULL);
|
||||
RefPhysicalAudioDevice(device); // reference it, in case the logical device migrates to a new default.
|
||||
}
|
||||
SDL_UnlockRWLock(current_audio.device_hash_lock);
|
||||
|
||||
if (logdev) {
|
||||
// we have to release the device_hash_lock before we take the device lock, to avoid deadlocks, so do a loop
|
||||
// to make sure the correct physical device gets locked, in case we're in a race with the default changing.
|
||||
while (SDL_TRUE) {
|
||||
SDL_LockMutex(device->lock);
|
||||
SDL_AudioDevice *recheck_device = (SDL_AudioDevice *) SDL_AtomicGetPtr((void **) &logdev->physical_device);
|
||||
if (device == recheck_device) {
|
||||
break;
|
||||
}
|
||||
|
||||
// default changed from under us! Try again!
|
||||
RefPhysicalAudioDevice(recheck_device);
|
||||
SDL_UnlockMutex(device->lock);
|
||||
UnrefPhysicalAudioDevice(device);
|
||||
device = recheck_device;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!logdev) {
|
||||
SDL_SetError("Invalid audio device instance ID");
|
||||
} else {
|
||||
SDL_assert(*device != NULL);
|
||||
SDL_LockMutex((*device)->lock);
|
||||
}
|
||||
|
||||
*_device = device;
|
||||
return logdev;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user