* GstBuffer and the GstBufferPool to get destroyed, so we are going to leak a
* fair ammount of memory.
*
- * Normally, this will never happen, even if we don't take special care for it,
- * because the compositor releases buffers almost immediately and when
- * waylandsink stops, they are already released.
+ * Normally, this rarely happens, because the compositor releases buffers
+ * almost immediately and when waylandsink stops, they are already released.
*
* However, we want to be absolutely certain, so a solution is introduced
* by registering all the GstWlBuffers with the display and explicitly
- * releasing all the buffer references and destroying the GstWlBuffers as soon
- * as the display is destroyed.
+ * releasing all the buffer references as soon as the display is destroyed.
+ *
+ * When the GstWlDisplay is finalized, it takes a reference to all the
+ * registered GstWlBuffers and then calls gst_wl_buffer_force_release_and_unref,
+ * which releases the potential reference to the GstBuffer, destroys the
+ * underlying wl_buffer and removes the reference that GstWlDisplay is holding.
+ * At that point, either the GstBuffer is alive somewhere and still holds a ref
+ * to the GstWlBuffer, which it will release when it gets destroyed, or the
+ * GstBuffer was destroyed in the meantime and the GstWlBuffer gets destroyed
+ * as soon as we remove the reference that GstWlDisplay holds.
*/
#include "wlbuffer.h"
static G_DEFINE_QUARK (GstWlBufferQDataQuark, gst_wl_buffer_qdata);
static void
-gst_wl_buffer_finalize (GObject * gobject)
+gst_wl_buffer_dispose (GObject * gobject)
{
GstWlBuffer *self = GST_WL_BUFFER (gobject);
+ GST_TRACE_OBJECT (self, "dispose");
+
+ /* if the display is shutting down and we are trying to dipose
+ * the GstWlBuffer from another thread, unregister_buffer() will
+ * block and in the end the display will increase the refcount
+ * of this GstWlBuffer, so it will not be finalized */
if (self->display)
gst_wl_display_unregister_buffer (self->display, self);
- wl_buffer_destroy (self->wlbuffer);
+
+ G_OBJECT_CLASS (gst_wl_buffer_parent_class)->dispose (gobject);
+}
+
+static void
+gst_wl_buffer_finalize (GObject * gobject)
+{
+ GstWlBuffer *self = GST_WL_BUFFER (gobject);
+
+ GST_TRACE_OBJECT (self, "finalize");
+
+ if (self->wlbuffer)
+ wl_buffer_destroy (self->wlbuffer);
G_OBJECT_CLASS (gst_wl_buffer_parent_class)->finalize (gobject);
}
{
GObjectClass *object_class = (GObjectClass *) klass;
+ object_class->dispose = gst_wl_buffer_dispose;
object_class->finalize = gst_wl_buffer_finalize;
}
buffer_release
};
+static void
+gstbuffer_disposed (GstWlBuffer * self)
+{
+ g_assert (!self->used_by_compositor);
+ self->gstbuffer = NULL;
+
+ GST_TRACE_OBJECT (self, "owning GstBuffer was finalized");
+
+ /* this will normally destroy the GstWlBuffer, unless the display is
+ * finalizing and it has taken an additional reference to it */
+ g_object_unref (self);
+}
+
GstWlBuffer *
gst_buffer_add_wl_buffer (GstBuffer * gstbuffer, struct wl_buffer *wlbuffer,
GstWlDisplay * display)
wl_buffer_add_listener (self->wlbuffer, &buffer_listener, self);
gst_mini_object_set_qdata ((GstMiniObject *) gstbuffer,
- gst_wl_buffer_qdata_quark (), self, g_object_unref);
+ gst_wl_buffer_qdata_quark (), self, (GDestroyNotify) gstbuffer_disposed);
return self;
}
void
gst_wl_buffer_force_release_and_unref (GstWlBuffer * self)
{
- /* detach from the GstBuffer */
- (void) gst_mini_object_steal_qdata ((GstMiniObject *) self->gstbuffer,
- gst_wl_buffer_qdata_quark ());
-
- /* force a buffer release
- * at this point, the GstWlDisplay has killed its event loop,
+ /* Force a buffer release.
+ * At this point, the GstWlDisplay has killed its event loop,
* so we don't need to worry about buffer_release() being called
* at the same time from the event loop thread */
if (self->used_by_compositor) {
GST_DEBUG_OBJECT (self, "forcing wl_buffer::release (GstBuffer: %p)",
self->gstbuffer);
- gst_buffer_unref (self->gstbuffer);
self->used_by_compositor = FALSE;
+ gst_buffer_unref (self->gstbuffer);
}
- /* avoid unregistering from the display in finalize() because this
- * function is being called from a hash table foreach function,
- * which would be modified in gst_wl_display_unregister_buffer() */
+ /* Finalize this GstWlBuffer early.
+ * This method has been called as a result of the display shutting down,
+ * so we need to stop using any wayland resources and disconnect from
+ * the display. The GstWlBuffer stays alive, though, to avoid race
+ * conditions with the GstBuffer being destroyed from another thread.
+ * The last reference is either owned by the GstBuffer or by us and
+ * it will be released at the end of this function. */
+ GST_TRACE_OBJECT (self, "finalizing early");
+ wl_buffer_destroy (self->wlbuffer);
+ self->wlbuffer = NULL;
self->display = NULL;
+
+ /* remove the reference that the caller (GstWlDisplay) owns */
g_object_unref (self);
}
self->formats = g_array_new (FALSE, FALSE, sizeof (uint32_t));
self->wl_fd_poll = gst_poll_new (TRUE);
self->buffers = g_hash_table_new (g_direct_hash, g_direct_equal);
+ g_mutex_init (&self->buffers_mutex);
}
static void
gst_poll_set_flushing (self->wl_fd_poll, TRUE);
g_thread_join (self->thread);
+ /* to avoid buffers being unregistered from another thread
+ * at the same time, take their ownership */
+ g_mutex_lock (&self->buffers_mutex);
+ self->shutting_down = TRUE;
+ g_hash_table_foreach (self->buffers, (GHFunc) g_object_ref, NULL);
+ g_mutex_unlock (&self->buffers_mutex);
+
g_hash_table_foreach (self->buffers,
(GHFunc) gst_wl_buffer_force_release_and_unref, NULL);
g_hash_table_remove_all (self->buffers);
g_array_unref (self->formats);
gst_poll_free (self->wl_fd_poll);
g_hash_table_unref (self->buffers);
+ g_mutex_clear (&self->buffers_mutex);
if (self->shm)
wl_shm_destroy (self->shm);
void
gst_wl_display_register_buffer (GstWlDisplay * self, gpointer buf)
{
+ g_assert (!self->shutting_down);
+
+ GST_TRACE_OBJECT (self, "registering GstWlBuffer %p", buf);
+
+ g_mutex_lock (&self->buffers_mutex);
g_hash_table_add (self->buffers, buf);
+ g_mutex_unlock (&self->buffers_mutex);
}
void
gst_wl_display_unregister_buffer (GstWlDisplay * self, gpointer buf)
{
- g_hash_table_remove (self->buffers, buf);
+ GST_TRACE_OBJECT (self, "unregistering GstWlBuffer %p", buf);
+
+ g_mutex_lock (&self->buffers_mutex);
+ if (G_LIKELY (!self->shutting_down))
+ g_hash_table_remove (self->buffers, buf);
+ g_mutex_unlock (&self->buffers_mutex);
}