pipewire: i/o callbacks should avoid higher-level iteration during device open.

Sometimes these callbacks will fire while we're still waiting on state to
settle down in PIPEWIRE_OpenDevice, which means we're holding the device lock,
but then the i/o callback will fire from a background thread and also try to
grab the device lock, but can't, because PIPEWIRE_OpenDevice is holding it and
waiting for this i/o callback to finish...hence, a deadlock.

So now, if the device is still opening, output callbacks will write silence
and input callbacks will just flush the buffer, without calling the main
iterate function, and thus avoid obtaining the lock.
This commit is contained in:
Ryan C. Gordon
2024-11-12 15:18:41 -05:00
parent 119b4fa5f5
commit 32cc92dceb

View File

@@ -939,7 +939,21 @@ static bool PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
static void output_callback(void *data)
{
SDL_PlaybackAudioThreadIterate((SDL_AudioDevice *)data);
SDL_AudioDevice *device = (SDL_AudioDevice *) data;
// this callback can fire in a background thread during OpenDevice, while we're still blocking
// _with the device lock_ until the stream is ready, causing a deadlock. Write silence in this case.
if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) {
int bufsize = 0;
Uint8 *buf = PIPEWIRE_GetDeviceBuf(device, &bufsize);
if (buf && bufsize) {
SDL_memset(buf, device->silence_value, bufsize);
}
PIPEWIRE_PlayDevice(device, buf, bufsize);
return;
}
SDL_PlaybackAudioThreadIterate(device);
}
static void PIPEWIRE_FlushRecording(SDL_AudioDevice *device)
@@ -980,7 +994,16 @@ static int PIPEWIRE_RecordDevice(SDL_AudioDevice *device, void *buffer, int bufl
static void input_callback(void *data)
{
SDL_RecordingAudioThreadIterate((SDL_AudioDevice *)data);
SDL_AudioDevice *device = (SDL_AudioDevice *) data;
// this callback can fire in a background thread during OpenDevice, while we're still blocking
// _with the device lock_ until the stream is ready, causing a deadlock. Drop data in this case.
if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) {
PIPEWIRE_FlushRecording(device);
return;
}
SDL_RecordingAudioThreadIterate(device);
}
static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer)