[glib/ebassi/dispose-notify: 2/2] Defer GObject::notify during object destruction




commit a4ea50cb6bf996c3af86c9e43850ce227bf48455
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Sun Nov 28 00:43:01 2021 +0000

    Defer GObject::notify during object destruction
    
    Notifying during object destruction is a dubious "feature": objects
    might end up recreating a bunch of state just before clearing it;
    language bindings might get spurious notifications during garbage
    collection runs.
    
    We freeze the notification queue before running the dispose() chain; if
    the object was temporarily vivified during dispose, we thaw the
    notification queue, otherwise we drain the queue right before running
    the finalize() chain.
    
    See: https://gitlab.gnome.org/GNOME/gjs/-/issues/445

 gobject/gobject.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)
---
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 33db8090c..dfe502604 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -350,12 +350,9 @@ g_object_notify_queue_thaw (GObject            *object,
 }
 
 static void
-g_object_notify_queue_drain (GObject            *object
+g_object_notify_queue_drain (GObject            *object,
                              GObjectNotifyQueue *nqueue)
 {
-  GSList *slist;
-  guint n_pspecs = 0;
-
   g_return_if_fail (g_atomic_int_get (&object->ref_count) > 0);
 
   G_LOCK (notify_lock);
@@ -3602,6 +3599,7 @@ g_object_unref (gpointer _object)
   else
     {
       GSList **weak_locations;
+      GObjectNotifyQueue *nqueue;
 
       /* The only way that this object can live at this point is if
        * there are outstanding weak references already established
@@ -3643,6 +3641,11 @@ g_object_unref (gpointer _object)
           g_rw_lock_writer_unlock (&weak_locations_lock);
         }
 
+      /* freeze the notification queue, so we don't accidentally emit
+       * notifications during dispose()
+       */
+      nqueue = g_object_notify_queue_freeze (object, TRUE);
+
       /* we are about to remove the last reference */
       TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 1));
       G_OBJECT_GET_CLASS (object)->dispose (object);
@@ -3656,6 +3659,9 @@ g_object_unref (gpointer _object)
           /* valid if last 2 refs are owned by this call to unref and the toggle_ref */
           gboolean has_toggle_ref = OBJECT_HAS_TOGGLE_REF (object);
 
+          /* emit all notifications that have been queued during dispose() */
+          g_object_notify_queue_thaw (object, nqueue);
+
           if (!g_atomic_int_compare_and_exchange ((int *)&object->ref_count, old_ref, old_ref - 1))
            goto retry_atomic_decrement2;
 
@@ -3683,6 +3689,12 @@ g_object_unref (gpointer _object)
       /* may have been re-referenced meanwhile */
       if (G_LIKELY (old_ref == 1))
        {
+          /* if there were notifications, it's too late to emit them now;
+           * any signal handler for the "notify" signal has been dropped
+           * anyhow
+           */
+          g_object_notify_queue_drain (object, nqueue);
+
          TRACE (GOBJECT_OBJECT_FINALIZE(object,G_TYPE_FROM_INSTANCE(object)));
           G_OBJECT_GET_CLASS (object)->finalize (object);
 


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