pipewire: i/o callbacks should avoid higher-level iteration during device open.
Some checks failed
Build (All) / Create test plan (push) Has been cancelled
Build (All) / level1 (push) Has been cancelled
Build (All) / level2 (push) Has been cancelled

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
No known key found for this signature in database
GPG key ID: FA148B892AB48044

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)