GNOME Bugzilla – Bug 760551
shmsink: Deadlock in memory free
Last modified: 2016-01-16 21:57:24 UTC
I discovered a deadlock during some stress testing of our video streaming architecture that utilizes shmsink/shmsrc to stream video buffers between two processes. The system in question was running with GStreamer 1.2.4, the version that ships with SuSE Linux Enterprise 12. A quick examination of git seems to indicate that the problematic code still exists in the current version of gst-plugins-bad. In our tests, a shmsrc client repeatedly connects/disconnects at various intervals to receive video from the shmsink server pipeline. At some point during the test, the shmsink element in the server process was observed to deadlock in gst_shm_sink_allocator_free(). The stack trace of the appropriate thread from gdb:
+ Trace 235894
The lock in question is the shmsink GST_OBJECT lock. A closer examination in gdb revealed that the same thread already held a lock on the mutex. Looking at the code, we found the initial GST_OBJECT_LOCK() being acquired at the start of gst_shm_sink_render(), but it isn't released until *after* a call to gst_memory_unref(): lines 714-721 in gstshmsink.c (exact line #s may vary depending on version): while (self->wait_for_connection && !self->clients) { g_cond_wait (&self->cond, GST_OBJECT_GET_LOCK (self)); if (self->unlock) { gst_memory_unref (memory); GST_OBJECT_UNLOCK (self); return GST_FLOW_FLUSHING; } } As can be seen in the stack trace, this gst_memory_unref() leads to calls to _gst_memory_free() and gst_shm_sink_allocator_free(). Inside of gst_shm_sink_allocator_free(), the code tries to acquire the shmsink GST_OBJECT_LOCK() again: static void gst_shm_sink_allocator_free (GstAllocator * allocator, GstMemory * mem) { GstShmSinkMemory *mymem = (GstShmSinkMemory *) mem; if (mymem->block) { GST_OBJECT_LOCK (mymem->sink); sp_writer_free_block (mymem->block); GST_OBJECT_UNLOCK (mymem->sink); gst_object_unref (mymem->sink); } gst_object_unref (mem->allocator); g_slice_free (GstShmSinkMemory, mymem); } I *think* this deadlock can be resolved by moving the gst_memory_unref() call in gst_shm_sink_render() to after the GST_OBJECT_UNLOCK() call, as in the following diff: @@ -714,8 +722,8 @@ while (self->wait_for_connection && !self->clients) { g_cond_wait (&self->cond, GST_OBJECT_GET_LOCK (self)); if (self->unlock) { - gst_memory_unref (memory); GST_OBJECT_UNLOCK (self); + gst_memory_unref (memory); return GST_FLOW_FLUSHING; } } We are currently testing this change in our own rebuilt version of the plugin and wanted to raise this issue with the GStreamer team to make sure they were aware of the problem and to get feedback on whether the proposed fix was appropriate.
Did you get a chance to try it in a more recent version? 1.6, 1.7 or git master? It seems like the same code path is still there, anyway. Could you provide the patch in git format-patch format so it can be merged? Thanks,
Created attachment 319115 [details] [review] Patch with a fix to deadlock when freeing memory in shmsink Patch made from gst_plugins_bad MASTER
I am unable to reproduce the problem nor test the fix on a more recent version. Our product is built against the GStreamer included in SLES 12, so that means integrating against anything other than 1.2.4 would be a significant task. Patch made against git master has been attached.
Review of attachment 319115 [details] [review]: That patch looks good.
commit eba01f84e53329dd14508cb72891c7a8e85888e0 Author: Matt Crane <mattcrane@tycoint.com> Date: Fri Jan 15 10:49:12 2016 -0500 shmsink: fix possible deadlock in _render()/ _allocator_free() Drop object lock before unrefing memory, otherwise the object lock might be taken again from the allocator and then things deadlock. https://bugzilla.gnome.org/show_bug.cgi?id=760551