Deadlock stopping alsa audiosink
I've occasionally had my app deadlock. The main thread is waiting for the alsasink thread to finish but the alsasink thread is stuck in the loop at https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/blob/master/ext/alsa/gstalsasink.c#L1069 and is not making any progress since alsa is paused and snd_pcm_writei()
returns 0 if it's in SND_PCM_STATE_PAUSED
.
I've been staring at the logic and I can't find anything that synchronizes between the sink thread and pausing. If the sink thread passes the gst_audio_ring_buffer_prepare_read()
check in https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/blob/master/gst-libs/gst/audio/gstaudiosink.c#L236, but another thread calls gst_alsasink_pause()
before snd_pcm_writei()
it can get into an infinite loop. The locking that happens at the ring buffer level doesn't impact the locking at the gstalsasink.
I've debugged this with 1.18.3 but a quick scan of 1.18.4 & HEAD doesn't show anything that I think will impact this.
I'm currently running with the following patch, which doesn't trigger during normal usage, but does allow it to recover. I'm not sure if it's the correct way to solve this issue. Perhaps the audioring should ensure it's out of the write loop before calling the next layer on pause.
diff -ru gst-plugins-base-1.18.3/ext/alsa/gstalsasink.c /var/tmp/portage/media-libs/gst-plugins-base-1.18.3/work/gst-plugins-base-1.18.3/ext/alsa/gstalsasink.c
--- gst-plugins-base-1.18.3/ext/alsa/gstalsasink.c 2021-01-13 16:07:13.997253000 -0500
+++ /var/tmp/portage/media-libs/gst-plugins-base-1.18.3/work/gst-plugins-base-1.18.3/ext/alsa/gstalsasink.c 2021-03-13 18:11:26.729060020 -0500
@@ -1068,6 +1068,14 @@
goto write_error;
}
continue;
+ } else if (err == 0 && alsa->hw_support_pause) {
+ /* We might be paused, if so, just bail */
+ snd_pcm_state_t state;
+
+ state = snd_pcm_state (alsa->handle);
+ GST_WARNING_OBJECT (asink, "TMPDBG: pause check. state = %i", state);
+ if (state == SND_PCM_STATE_PAUSED)
+ break;
}
ptr += snd_pcm_frames_to_bytes (alsa->handle, err);
diff -ru gst-plugins-base-1.18.3/gst-libs/gst/audio/gstaudiosink.c /var/tmp/portage/media-libs/gst-plugins-base-1.18.3/work/gst-plugins-base-1.18.3/gst-libs/gst/audio/gstaudiosink.c
--- gst-plugins-base-1.18.3/gst-libs/gst/audio/gstaudiosink.c 2021-01-13 16:07:14.021253000 -0500
+++ /var/tmp/portage/media-libs/gst-plugins-base-1.18.3/work/gst-plugins-base-1.18.3/gst-libs/gst/audio/gstaudiosink.c 2021-03-15 15:03:21.496837689 -0400
@@ -254,7 +254,12 @@
GST_DEBUG_FUNCPTR_NAME (writefunc),
(errno > 1 ? g_strerror (errno) : "unknown"), left, written);
break;
+ } else if (written == 0 && G_UNLIKELY (g_atomic_int_get (&buf->state) !=
+ GST_AUDIO_RING_BUFFER_STATE_STARTED)) {
+ GST_WARNING_OBJECT (sink, "TMPDBG: started check.");
+ break;
}
+
left -= written;
readptr += written;
} while (left > 0);