Merge branch 'bindung-unbind-fix' into 'master'

g_binding_unbind: make it more introspection friendly; allow calling it multiple times. Fixes #1373

Closes #1373

See merge request GNOME/glib!244
diff --git a/gobject/gbinding.c b/gobject/gbinding.c
index 6872b96..42dcb36 100644
--- a/gobject/gbinding.c
+++ b/gobject/gbinding.c
@@ -373,6 +373,7 @@
                            gboolean  unref_binding)
 {
   gboolean source_is_target = binding->source == binding->target;
+  gboolean binding_was_removed = FALSE;
 
   /* dispose of the transformation data */
   if (binding->notify != NULL)
@@ -392,6 +393,7 @@
 
       binding->source_notify = 0;
       binding->source = NULL;
+      binding_was_removed = TRUE;
     }
 
   if (binding->target != NULL)
@@ -404,9 +406,10 @@
 
       binding->target_notify = 0;
       binding->target = NULL;
+      binding_was_removed = TRUE;
     }
 
-  if (unref_binding)
+  if (binding_was_removed && unref_binding)
     g_object_unref (binding);
 }
 
@@ -748,7 +751,7 @@
 
 /**
  * g_binding_unbind:
- * @binding: (transfer full): a #GBinding
+ * @binding: a #GBinding
  *
  * Explicitly releases the binding between the source and the target
  * property expressed by @binding.
diff --git a/gobject/tests/binding.c b/gobject/tests/binding.c
index e088ca7..019fb28 100644
--- a/gobject/tests/binding.c
+++ b/gobject/tests/binding.c
@@ -460,6 +460,7 @@
   BindingSource *c = g_object_new (binding_source_get_type (), NULL);
   GBinding *binding_1, *binding_2;
 
+  g_test_bug_base ("http://bugzilla.gnome.org/");
   g_test_bug ("621782");
 
   /* A -> B, B -> C */
@@ -625,6 +626,83 @@
   g_object_unref (source);
 }
 
+/* When source or target die, so does the binding if there is no other ref */
+static void
+binding_unbind_weak (void)
+{
+  GBinding *binding;
+  BindingSource *source;
+  BindingTarget *target;
+
+  /* first source, then target */
+  source = g_object_new (binding_source_get_type (), NULL);
+  target = g_object_new (binding_target_get_type (), NULL);
+  binding = g_object_bind_property (source, "foo",
+                                    target, "bar",
+                                    G_BINDING_DEFAULT);
+  g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
+  g_assert_nonnull (binding);
+  g_object_unref (source);
+  g_assert_null (binding);
+  g_object_unref (target);
+  g_assert_null (binding);
+
+  /* first target, then source */
+  source = g_object_new (binding_source_get_type (), NULL);
+  target = g_object_new (binding_target_get_type (), NULL);
+  binding = g_object_bind_property (source, "foo",
+                                    target, "bar",
+                                    G_BINDING_DEFAULT);
+  g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
+  g_assert_nonnull (binding);
+  g_object_unref (target);
+  g_assert_null (binding);
+  g_object_unref (source);
+  g_assert_null (binding);
+
+  /* target and source are the same */
+  source = g_object_new (binding_source_get_type (), NULL);
+  binding = g_object_bind_property (source, "foo",
+                                    source, "bar",
+                                    G_BINDING_DEFAULT);
+  g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
+  g_assert_nonnull (binding);
+  g_object_unref (source);
+  g_assert_null (binding);
+}
+
+/* Test that every call to unbind() after the first is a noop */
+static void
+binding_unbind_multiple (void)
+{
+  BindingSource *source = g_object_new (binding_source_get_type (), NULL);
+  BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
+  GBinding *binding;
+  guint i;
+
+  g_test_bug ("1373");
+
+  binding = g_object_bind_property (source, "foo",
+                                    target, "bar",
+                                    G_BINDING_DEFAULT);
+  g_object_ref (binding);
+  g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
+  g_assert_nonnull (binding);
+
+  /* this shouldn't crash */
+  for (i = 0; i < 50; i++)
+    {
+      g_binding_unbind (binding);
+      g_assert_nonnull (binding);
+    }
+
+  g_object_unref (binding);
+  g_assert_null (binding);
+
+  g_object_unref (source);
+  g_object_unref (target);
+}
+
 static void
 binding_fail (void)
 {
@@ -653,7 +731,7 @@
 {
   g_test_init (&argc, &argv, NULL);
 
-  g_test_bug_base ("http://bugzilla.gnome.org/");
+  g_test_bug_base ("https://gitlab.gnome.org/GNOME/glib/issues/");
 
   g_test_add_func ("/binding/default", binding_default);
   g_test_add_func ("/binding/bidirectional", binding_bidirectional);
@@ -665,6 +743,8 @@
   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);
+  g_test_add_func ("/binding/unbind-weak", binding_unbind_weak);
+  g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple);
   g_test_add_func ("/binding/fail", binding_fail);
 
   return g_test_run ();