GNOME Bugzilla – Bug 473846
Deadlock if sink is unable to send first buffer
Last modified: 2007-09-05 22:16:10 UTC
Please describe the problem: Unlock function is not called on a sink that sink is based on basesink. This occurs when that sink is added to an pipeline that is already in PLAY state (the sink is brought up to PLAY before it is added) and the sink is unable to pass on its first buffer (i.e. it never returns). Steps to reproduce: 1. Add a source to a pipeline 2. Set the pipeline to PLAYING state 3. Create a sink (based on basesink) that does not return in render. 4. Set the state of the sink to PLAYING 5. Add sink to pipeline. 6. Try to set state of pipeline to NULL 7. App will hang on STATE_LOCK Actual results: 1. The GST_PAD_PREROLL_LOCK for the sink is taken in gst_base_sink_chain, initiated by a gst_pad_push. 2. Since the sink is unable to push its first buffer the lock will not be released. 3. The GST_STATE_LOCK for the pipeline is taken in gst_bin_continue_func which then performs a latency query and then hangs on the sinks GST_PAD_PREROLL_LOCK (taken in 1.). 4. When the state is changed to NULL the gst_element_set_state_func will hang on on the previously taken GST_STATE_LOCK. Expected results: A deadlock occurs. Does this happen every time? yes Other information: /* * A small test application that simulates the bug with an fdsink writing more * then 64K to a pipe and therefore blocks the first buffer. */ #include <gst/gst.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main (int argc, char *argv[]) { GstStateChangeReturn sret; GstElement *pipeline; GstElement *source, *sink; gint fd; gst_init (&argc, &argv); if (unlink("test")) { perror("unlink:"); } if (mkfifo("test", O_CREAT|S_IRWXU)) { perror("mkfifo:"); } fd = open("test", O_RDWR); pipeline = gst_pipeline_new ("pipeline"); source = gst_element_factory_make ("fakesrc", "source"); g_object_set (G_OBJECT (source), "num-buffers", 5, NULL); g_object_set (G_OBJECT (source), "sizetype", 2, NULL); g_object_set (G_OBJECT (source), "filltype", 4, NULL); g_object_set (G_OBJECT (source), "sizemax", 70000, NULL); gst_bin_add (GST_BIN (pipeline), source); sret = gst_element_set_state (pipeline, GST_STATE_PLAYING); sink = gst_element_factory_make ("fdsink", "sink"); g_object_set (G_OBJECT (sink), "fd", fd, NULL); gst_bin_add (GST_BIN (pipeline), sink); gst_element_set_state (sink, GST_STATE_PLAYING); gst_element_link (source, sink); /* To make sure that the deadlock occurs */ sleep (4); gst_element_set_state (pipeline, GST_STATE_NULL); return 0; }
two problems with this program: 1) you start playback of the source before it is linked, this causes an error if you don't manage to link the sink fast enough. 2) the fd passed to fdsink is not set to NONBLOCK, this currently makes the write call block (and not cancelable) forever. I think you just want to make a pipeline, set it to PLAYING, then perform a latency query and see it deadlock. I changed core to not take the PREROLL lock anymore when doing the query. * libs/gst/base/gstbasesink.c: (gst_base_sink_preroll_queue_flush), (gst_base_sink_wait_preroll), (gst_base_sink_needs_preroll), (gst_base_sink_query): Protect eos and have_preroll with the OBJECT lock so we don't need to take the PREROLL lock when querying the latency. Fixes #473846.