decodebin: possible fix for deadlock when spamming "next song"
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Tue, 3 Feb 2015 17:06:43 +0000 (17:06 +0000)
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Thu, 19 Feb 2015 13:38:29 +0000 (13:38 +0000)
There was a deadlock between a thread changing decodebin/demuxer
state from PAUSED to READY, and another thread pushing data
when starting.

From the stack trace at
https://bug741355.bugzilla-attachments.gnome.org/attachment.cgi?id=292471,
I deduce the following is happening, though I did not reproduce the
problem so I'm not sure this patch fixes it.

The streaming thread (thread 2 in that stack trace) takes the demuxer's
sink pad's stream lock in gst_ogg_demux_perform_seek_pull and will
activate a new chain. This ends up causing the expose lock being taken
in _pad_added_cb in decodebin.

Meanwhile, a state changed is triggered on thread 1, which takes the
expose lock in decodebin in gst_decode_bin_change_state, then frees
the previous chain, which ends up calling gst_pad_stop_task on the
demuxer's task, which in turn takes the demuxer's sink pad's stream
lock, deadlocking as both threads are now waiting for each other.

https://bugzilla.gnome.org/show_bug.cgi?id=741355

gst/playback/gstdecodebin2.c

index 00e5132..87dd5dd 100644 (file)
@@ -4853,6 +4853,7 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
 {
   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
   GstDecodeBin *dbin = GST_DECODE_BIN (element);
+  GstDecodeChain *chain_to_free = NULL;
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
@@ -4911,10 +4912,13 @@ gst_decode_bin_change_state (GstElement * element, GstStateChange transition)
       do_async_done (dbin);
       EXPOSE_LOCK (dbin);
       if (dbin->decode_chain) {
-        gst_decode_chain_free (dbin->decode_chain);
+        chain_to_free = dbin->decode_chain;
+        gst_decode_chain_free_internal (dbin->decode_chain, TRUE);
         dbin->decode_chain = NULL;
       }
       EXPOSE_UNLOCK (dbin);
+      if (chain_to_free)
+        gst_decode_chain_free (chain_to_free);
       g_list_free_full (dbin->buffering_status,
           (GDestroyNotify) gst_message_unref);
       dbin->buffering_status = NULL;