[glib: 2/3] Handle the case of g_object_run_dispose() in GBinding




commit e82eb490fea312ebe30e117288fc2e3bf2378a25
Author: Sebastian Dröge <sebastian centricular com>
Date:   Tue Dec 8 18:36:16 2020 +0200

    Handle the case of g_object_run_dispose() in GBinding
    
    When this is called on the source or target, the weak notify of the
    corresponding object is called without the GWeakRef being cleared.
    See https://gitlab.gnome.org/GNOME/glib/-/issues/2266 for that issue.
    
    This means that a strong reference to these zombie objects can be
    retrieved from the GWeakRefs and the previous assumption that this can't
    happen was wrong. Remove the assertion for that accordingly and handle
    this case.
    
    Specifically, all signal handlers and weak notifies of the object are
    already gone and must not be disconnected/removed a second time, or
    otherwise memory corruption would be caused. Instead just set the
    GWeakRef to NULL and handle it otherwise as if the GWeakRef didn't give
    a strong reference to begin with.
    
    Fixes https://gitlab.gnome.org/GNOME/glib/-/issues/2265

 gobject/gbinding.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)
---
diff --git a/gobject/gbinding.c b/gobject/gbinding.c
index 562f339da..48b4fbaec 100644
--- a/gobject/gbinding.c
+++ b/gobject/gbinding.c
@@ -388,11 +388,30 @@ weak_unbind (gpointer  user_data,
   target = g_weak_ref_get (&context->target);
 
   /* If this is called then either the source or target or both must be in the
-   * process of being finalized and their weak reference must be reset to NULL
-   * already.
+   * process of being disposed. If this happens as part of g_object_unref()
+   * then the weak references are actually cleared, otherwise if disposing
+   * happens as part of g_object_run_dispose() then they would still point to
+   * the disposed object.
    *
-   * If source==target then both will always be NULL here. */
-  g_assert (source == NULL || target == NULL);
+   * If the object this is being called for is either the source or the target
+   * and we actually got a strong reference to it nonetheless (see above),
+   * then signal handlers and weak notifies for it are already disconnected
+   * and they must not be disconnected a second time. Instead simply clear the
+   * weak reference and be done with it.
+   *
+   * See https://gitlab.gnome.org/GNOME/glib/-/issues/2266 */
+
+  if (source == where_the_object_was)
+    {
+      g_weak_ref_set (&context->source, NULL);
+      g_clear_object (&source);
+    }
+
+  if (target == where_the_object_was)
+    {
+      g_weak_ref_set (&context->target, NULL);
+      g_clear_object (&target);
+    }
 
   binding_was_removed = unbind_internal_locked (context, binding, source, target);
 
@@ -627,10 +646,6 @@ g_binding_unbind_internal (GBinding *binding,
   source = g_weak_ref_get (&context->source);
   target = g_weak_ref_get (&context->target);
 
-  /* If the binding was removed previously, source and target are both NULL.
-   * Otherwise both will not be NULL. */
-  g_assert ((source == NULL && target == NULL) || (source != NULL && target != NULL));
-
   binding_was_removed = unbind_internal_locked (context, binding, source, target);
 
   g_mutex_unlock (&binding->unbind_lock);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]