From a360b314aad5dcbc575948a401d22d47a36e4328 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Sun, 14 Apr 2013 21:04:41 +0100 Subject: [PATCH] binding: Add an explicit unbind() Higher order languages with garbage collection can have issues releasing a binding, as they do not control the last reference being dropped on the binding, source, or target instances. https://bugzilla.gnome.org/show_bug.cgi?id=698018 --- docs/reference/gobject/gobject-sections.txt | 1 + gobject/gbinding.c | 89 +++++++++++++++++++---------- gobject/gbinding.h | 2 + gobject/tests/binding.c | 32 +++++++++++ 4 files changed, 95 insertions(+), 29 deletions(-) diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index d507f15..56d0e22 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -935,6 +935,7 @@ g_binding_get_source_property g_binding_get_target g_binding_get_target_property g_binding_get_flags +g_binding_release g_object_bind_property GBindingTransformFunc diff --git a/gobject/gbinding.c b/gobject/gbinding.c index 3dbe616..714bb1c 100644 --- a/gobject/gbinding.c +++ b/gobject/gbinding.c @@ -96,6 +96,11 @@ * either one of the #GObject instances it refers to are finalized, or when * the #GBinding instance loses its last reference. * + * Bindings for languages with garbage collection can use + * g_binding_unbind() to explicitly release a binding between the source + * and target properties, instead of relying on the last reference on the + * binding, source, and target instances to drop. + * * #GBinding is available since GObject 2.26 */ @@ -244,6 +249,8 @@ weak_unbind (gpointer user_data, g_object_weak_unref (binding->source, weak_unbind, user_data); remove_binding_qdata (binding->source, binding); + + binding->source_notify = 0; binding->source = NULL; } @@ -257,6 +264,8 @@ weak_unbind (gpointer user_data, g_object_weak_unref (binding->target, weak_unbind, user_data); remove_binding_qdata (binding->target, binding); + + binding->target_notify = 0; binding->target = NULL; } @@ -431,35 +440,7 @@ g_binding_finalize (GObject *gobject) { GBinding *binding = G_BINDING (gobject); - /* dispose of the transformation data */ - if (binding->notify != NULL) - { - binding->notify (binding->transform_data); - - binding->transform_data = NULL; - binding->notify = NULL; - } - - /* we need this in case the source and target instance are still - * valid, and it was the GBinding that was unreferenced - */ - if (binding->source != NULL) - { - if (binding->source_notify != 0) - g_signal_handler_disconnect (binding->source, binding->source_notify); - - g_object_weak_unref (binding->source, weak_unbind, binding); - remove_binding_qdata (binding->source, binding); - } - - if (binding->target != NULL) - { - if (binding->target_notify != 0) - g_signal_handler_disconnect (binding->target, binding->target_notify); - - g_object_weak_unref (binding->target, weak_unbind, binding); - remove_binding_qdata (binding->target, binding); - } + g_binding_unbind (binding); G_OBJECT_CLASS (g_binding_parent_class)->finalize (gobject); } @@ -772,6 +753,56 @@ g_binding_get_target_property (GBinding *binding) } /** + * g_binding_unbind: + * @binding: a #GBinding + * + * Explicitly releases the binding between the source and the target + * property expressed by @binding. + * + * This function does not change the reference count of @binding. + * + * Since: 2.38 + */ +void +g_binding_unbind (GBinding *binding) +{ + g_return_if_fail (G_IS_BINDING (binding)); + + /* dispose of the transformation data */ + if (binding->notify != NULL) + { + binding->notify (binding->transform_data); + + binding->transform_data = NULL; + binding->notify = NULL; + } + + if (binding->source != NULL) + { + if (binding->source_notify != 0) + g_signal_handler_disconnect (binding->source, binding->source_notify); + + g_object_weak_unref (binding->source, weak_unbind, binding); + remove_binding_qdata (binding->source, binding); + + binding->source_notify = 0; + binding->source = NULL; + } + + if (binding->target != NULL) + { + if (binding->target_notify != 0) + g_signal_handler_disconnect (binding->target, binding->target_notify); + + g_object_weak_unref (binding->target, weak_unbind, binding); + remove_binding_qdata (binding->target, binding); + + binding->target_notify = 0; + binding->target = NULL; + } +} + +/** * g_object_bind_property_full: * @source: (type GObject.Object): the source #GObject * @source_property: the property on @source to bind diff --git a/gobject/gbinding.h b/gobject/gbinding.h index ebb329c..e49ad6d 100644 --- a/gobject/gbinding.h +++ b/gobject/gbinding.h @@ -115,6 +115,8 @@ GLIB_AVAILABLE_IN_ALL const gchar * g_binding_get_source_property (GBinding *binding); GLIB_AVAILABLE_IN_ALL const gchar * g_binding_get_target_property (GBinding *binding); +GLIB_AVAILABLE_IN_2_38 +void g_binding_unbind (GBinding *binding); GLIB_AVAILABLE_IN_ALL GBinding *g_object_bind_property (gpointer source, diff --git a/gobject/tests/binding.c b/gobject/tests/binding.c index b6dcdbb..7f7121e 100644 --- a/gobject/tests/binding.c +++ b/gobject/tests/binding.c @@ -570,6 +570,37 @@ binding_same_object (void) g_object_unref (source); } +static void +binding_unbind (void) +{ + BindingSource *source = g_object_new (binding_source_get_type (), NULL); + BindingTarget *target = g_object_new (binding_target_get_type (), NULL); + GBinding *binding; + + binding = g_object_bind_property (source, "foo", + target, "bar", + G_BINDING_DEFAULT); + g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding); + + + g_object_set (source, "foo", 42, NULL); + g_assert_cmpint (source->foo, ==, target->bar); + + g_object_set (target, "bar", 47, NULL); + g_assert_cmpint (source->foo, !=, target->bar); + + g_binding_unbind (binding); + g_assert (binding != NULL); + + g_object_set (source, "foo", 0, NULL); + g_assert_cmpint (source->foo, !=, target->bar); + + g_object_unref (source); + g_object_unref (target); + g_object_unref (binding); + g_assert (binding == NULL); +} + int main (int argc, char *argv[]) { @@ -586,6 +617,7 @@ main (int argc, char *argv[]) g_test_add_func ("/binding/sync-create", binding_sync_create); g_test_add_func ("/binding/invert-boolean", binding_invert_boolean); g_test_add_func ("/binding/same-object", binding_same_object); + g_test_add_func ("/binding/unbind", binding_unbind); return g_test_run (); } -- 2.7.4