From cf671d7b0a89b24b14028a152cde8ff8e612d84e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Alburquerque?= Date: Wed, 11 May 2011 13:09:19 -0400 Subject: [PATCH] miniobject: Add weak referencing functionality API: gst_mini_object_weak_ref() API: gst_mini_object_weak_unref() Add weak referencing functionality to GstMiniObject, which allows to get notifications when an mini object is destroyed but doesn't increase the real refcount. This is mostly useful for bindings. Fixes bug #609473. --- gst/gstminiobject.c | 151 +++++++++++++++++++++++++++++++- gst/gstminiobject.h | 24 ++++- tests/check/gst/gstminiobject.c | 28 ++++++ 3 files changed, 198 insertions(+), 5 deletions(-) diff --git a/gst/gstminiobject.c b/gst/gstminiobject.c index 4b46d29297..0fe5a8c403 100644 --- a/gst/gstminiobject.c +++ b/gst/gstminiobject.c @@ -46,6 +46,24 @@ static GstAllocTrace *_gst_mini_object_trace; #define GST_MINI_OBJECT_GET_CLASS_UNCHECKED(obj) \ ((GstMiniObjectClass *) (((GTypeInstance*)(obj))->g_class)) +/* Structure used for storing weak references */ +typedef struct +{ + GstMiniObject *object; + guint n_weak_refs; + struct + { + GstMiniObjectWeakNotify notify; + gpointer data; + } weak_refs[1]; /* flexible array */ +} WeakRefStack; + +/* Structure for storing a mini object's private data */ +struct _GstMiniObjectPrivateData +{ + WeakRefStack *wstack; +}; + #if 0 static void gst_mini_object_base_init (gpointer g_class); static void gst_mini_object_base_finalize (gpointer g_class); @@ -55,6 +73,7 @@ static void gst_mini_object_init (GTypeInstance * instance, gpointer klass); static void gst_value_mini_object_init (GValue * value); static void gst_value_mini_object_free (GValue * value); +static void weak_refs_notify (WeakRefStack * data); static void gst_value_mini_object_copy (const GValue * src_value, GValue * dest_value); static gpointer gst_value_mini_object_peek_pointer (const GValue * value); @@ -66,6 +85,9 @@ static gchar *gst_value_mini_object_lcopy (const GValue * value, static GstMiniObject *gst_mini_object_copy_default (const GstMiniObject * obj); static void gst_mini_object_finalize (GstMiniObject * obj); +/* Mutex used for weak referencing */ +G_LOCK_DEFINE_STATIC (weak_refs_mutex); + GType gst_mini_object_get_type (void) { @@ -138,6 +160,9 @@ gst_mini_object_class_init (gpointer g_class, gpointer class_data) mo_class->copy = gst_mini_object_copy_default; mo_class->finalize = gst_mini_object_finalize; + + /* Set the instance data type */ + g_type_class_add_private (g_class, sizeof (GstMiniObjectPrivateData)); } static void @@ -146,6 +171,14 @@ gst_mini_object_init (GTypeInstance * instance, gpointer klass) GstMiniObject *mini_object = GST_MINI_OBJECT_CAST (instance); mini_object->refcount = 1; + + /* Initialize the mini object's private data */ + + mini_object->priv = (GstMiniObjectPrivateData *) + G_TYPE_INSTANCE_GET_PRIVATE (instance, GST_TYPE_MINI_OBJECT, + GstMiniObjectPrivateData); + + mini_object->priv->wstack = NULL; } static GstMiniObject * @@ -320,6 +353,16 @@ gst_mini_object_ref (GstMiniObject * mini_object) return mini_object; } +static void +weak_refs_notify (WeakRefStack * wstack) +{ + guint i; + + for (i = 0; i < wstack->n_weak_refs; i++) + wstack->weak_refs[i].notify (wstack->weak_refs[i].data, wstack->object); + g_free (wstack); +} + static void gst_mini_object_free (GstMiniObject * mini_object) { @@ -340,6 +383,10 @@ gst_mini_object_free (GstMiniObject * mini_object) /* decrement the refcount again, if the subclass recycled the object we don't * want to free the instance anymore */ if (G_LIKELY (g_atomic_int_dec_and_test (&mini_object->refcount))) { + /* The weak reference stack is freed in the notification function */ + if (mini_object->priv->wstack) + weak_refs_notify (mini_object->priv->wstack); + #ifndef GST_DISABLE_TRACE gst_alloc_trace_free (_gst_mini_object_trace, mini_object); #endif @@ -370,6 +417,102 @@ gst_mini_object_unref (GstMiniObject * mini_object) } } +/** + * gst_mini_object_weak_ref: (skip) + * @mini_object: #GstMiniObject to reference weakly + * @notify: callback to invoke before the mini object is freed + * @data: extra data to pass to notify + * + * Adds a weak reference callback to a mini object. Weak references are + * used for notification when a mini object is finalized. They are called + * "weak references" because they allow you to safely hold a pointer + * to the mini object without calling gst_mini_object_ref() + * (gst_mini_object_ref() adds a strong reference, that is, forces the object + * to stay alive). + * + * Since: 0.10.34 + */ +void +gst_mini_object_weak_ref (GstMiniObject * object, + GstMiniObjectWeakNotify notify, gpointer data) +{ + guint i; + + g_return_if_fail (GST_IS_MINI_OBJECT (object)); + g_return_if_fail (notify != NULL); + g_return_if_fail (GST_MINI_OBJECT_REFCOUNT_VALUE (object) >= 1); + + G_LOCK (weak_refs_mutex); + + if (object->priv->wstack) { + /* Don't add the weak reference if it already exists. */ + for (i = 0; i < object->priv->wstack->n_weak_refs; i++) { + if (object->priv->wstack->weak_refs[i].notify == notify && + object->priv->wstack->weak_refs[i].data == data) { + g_warning ("%s: Attempt to re-add existing weak ref %p(%p) failed.", + G_STRFUNC, notify, data); + goto found; + } + } + + i = object->priv->wstack->n_weak_refs++; + object->priv->wstack = + g_realloc (object->priv->wstack, sizeof (*(object->priv->wstack)) + + sizeof (object->priv->wstack->weak_refs[0]) * i); + } else { + object->priv->wstack = g_renew (WeakRefStack, NULL, 1); + object->priv->wstack->object = object; + object->priv->wstack->n_weak_refs = 1; + i = 0; + } + object->priv->wstack->weak_refs[i].notify = notify; + object->priv->wstack->weak_refs[i].data = data; +found: + G_UNLOCK (weak_refs_mutex); +} + +/** + * gst_mini_object_weak_unref: (skip) + * @mini_object: #GstMiniObject to remove a weak reference from + * @notify: callback to search for + * @data: data to search for + * + * Removes a weak reference callback to a mini object. + * + * Since: 0.10.34 + */ +void +gst_mini_object_weak_unref (GstMiniObject * object, + GstMiniObjectWeakNotify notify, gpointer data) +{ + gboolean found_one = FALSE; + + g_return_if_fail (GST_IS_MINI_OBJECT (object)); + g_return_if_fail (notify != NULL); + + G_LOCK (weak_refs_mutex); + + if (object->priv->wstack) { + guint i; + + for (i = 0; i < object->priv->wstack->n_weak_refs; i++) + if (object->priv->wstack->weak_refs[i].notify == notify && + object->priv->wstack->weak_refs[i].data == data) { + found_one = TRUE; + object->priv->wstack->n_weak_refs -= 1; + if (i != object->priv->wstack->n_weak_refs) + object->priv->wstack->weak_refs[i] = + object->priv->wstack->weak_refs[object->priv->wstack-> + n_weak_refs]; + + break; + } + } + G_UNLOCK (weak_refs_mutex); + if (!found_one) + g_warning ("%s: couldn't find weak ref %p(%p)", G_STRFUNC, notify, data); +} + /** * gst_mini_object_replace: * @olddata: (inout) (transfer full): pointer to a pointer to a mini-object to @@ -427,8 +570,8 @@ gst_value_mini_object_copy (const GValue * src_value, GValue * dest_value) { if (src_value->data[0].v_pointer) { dest_value->data[0].v_pointer = - gst_mini_object_ref (GST_MINI_OBJECT_CAST (src_value-> - data[0].v_pointer)); + gst_mini_object_ref (GST_MINI_OBJECT_CAST (src_value->data[0]. + v_pointer)); } else { dest_value->data[0].v_pointer = NULL; } @@ -556,8 +699,8 @@ gst_value_dup_mini_object (const GValue * value) { g_return_val_if_fail (GST_VALUE_HOLDS_MINI_OBJECT (value), NULL); - return value->data[0].v_pointer ? gst_mini_object_ref (value->data[0]. - v_pointer) : NULL; + return value->data[0].v_pointer ? gst_mini_object_ref (value-> + data[0].v_pointer) : NULL; } diff --git a/gst/gstminiobject.h b/gst/gstminiobject.h index 1005dc5442..fbc7801ac4 100644 --- a/gst/gstminiobject.h +++ b/gst/gstminiobject.h @@ -135,6 +135,22 @@ typedef enum */ #define GST_MINI_OBJECT_REFCOUNT_VALUE(obj) (g_atomic_int_get (&(GST_MINI_OBJECT_CAST(obj))->refcount)) +/** + * GstMiniObjectWeakNotify: + * @data: data that was provided when the weak reference was established + * @where_the_mini_object_was: the mini object being finalized + * + * A #GstMiniObjectWeakNotify function can be added to a mini object as a + * callback that gets triggered when the mini object is finalized. Since the + * mini object is already being finalized when the #GstMiniObjectWeakNotify is + * called, there's not much you could do with the object, apart from e.g. using + * its adress as hash-index or the like. + */ +typedef void (*GstMiniObjectWeakNotify) (gpointer data, + GstMiniObject * where_the_mini_object_was); + +typedef struct _GstMiniObjectPrivateData GstMiniObjectPrivateData; + /** * GstMiniObject: * @instance: type instance @@ -154,7 +170,7 @@ struct _GstMiniObject { guint flags; /*< private >*/ - gpointer _gst_reserved; + GstMiniObjectPrivateData *priv; }; struct _GstMiniObjectClass { @@ -177,6 +193,12 @@ GstMiniObject* gst_mini_object_make_writable (GstMiniObject *mini_object); /* refcounting */ GstMiniObject* gst_mini_object_ref (GstMiniObject *mini_object); void gst_mini_object_unref (GstMiniObject *mini_object); +void gst_mini_object_weak_ref (GstMiniObject *object, + GstMiniObjectWeakNotify notify, + gpointer data); +void gst_mini_object_weak_unref (GstMiniObject *object, + GstMiniObjectWeakNotify notify, + gpointer data); void gst_mini_object_replace (GstMiniObject **olddata, GstMiniObject *newdata); /* GParamSpec */ diff --git a/tests/check/gst/gstminiobject.c b/tests/check/gst/gstminiobject.c index 9b9c1cde9c..0a7a7313b2 100644 --- a/tests/check/gst/gstminiobject.c +++ b/tests/check/gst/gstminiobject.c @@ -175,6 +175,33 @@ GST_START_TEST (test_unref_threaded) GST_END_TEST; +/* ======== weak ref test ======== */ + +static gboolean weak_ref_notify_succeeded = FALSE; + +static void +on_weak_ref_notify (gpointer data, GstMiniObject * where_object_was) +{ + weak_ref_notify_succeeded = TRUE; +} + +GST_START_TEST (test_weak_ref) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new_and_alloc (4); + + gst_mini_object_weak_ref (GST_MINI_OBJECT (buffer), on_weak_ref_notify, + &buffer); + + gst_buffer_unref (buffer); + + fail_unless (weak_ref_notify_succeeded, + "No weak reference notification took place."); +} + +GST_END_TEST; + /* ======== recycle test ======== */ static gint recycle_buffer_count = 10; @@ -479,6 +506,7 @@ gst_mini_object_suite (void) tcase_add_test (tc_chain, test_make_writable); tcase_add_test (tc_chain, test_ref_threaded); tcase_add_test (tc_chain, test_unref_threaded); + tcase_add_test (tc_chain, test_weak_ref); tcase_add_test (tc_chain, test_recycle_threaded); tcase_add_test (tc_chain, test_value_collection); tcase_add_test (tc_chain, test_dup_null_mini_object); -- 2.34.1