[glib] GWeakRef: add a weak GObject reference believed to be thread-safe



commit 46c2f570da10c93d7b2fab790048f957bb422302
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Dec 6 12:27:07 2011 +0000

    GWeakRef: add a weak GObject reference believed to be thread-safe
    
    This patch is a joint work with Simon McVittie.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=548954

 gobject/gobject.c       |  239 ++++++++++++++++++++++++++++++++++++++++++++++-
 gobject/gobject.h       |   12 +++
 gobject/gobject.symbols |    4 +
 3 files changed, 253 insertions(+), 2 deletions(-)
---
diff --git a/gobject/gobject.c b/gobject/gobject.c
index a8d4f9e..ad75a30 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -200,6 +200,9 @@ static gulong	            gobject_signals[LAST_SIGNAL] = { 0, };
 static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;
 G_LOCK_DEFINE_STATIC (construction_mutex);
 static GSList *construction_objects = NULL;
+/* qdata pointing to GSList<GWeakRef *>, protected by weak_locations_lock */
+static GQuark	            quark_weak_locations = 0;
+static GRWLock              weak_locations_lock;
 
 G_LOCK_DEFINE_STATIC(notify_lock);
 
@@ -434,6 +437,7 @@ g_object_do_class_init (GObjectClass *class)
   quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
 
   quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
+  quark_weak_locations = g_quark_from_static_string ("GObject-weak-locations");
   quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
   quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
   pspec_pool = g_param_spec_pool_new (TRUE);
@@ -2466,6 +2470,7 @@ weak_refs_notify (gpointer data)
  * Note that the weak references created by this method are not
  * thread-safe: they cannot safely be used in one thread if the
  * object's last g_object_unref() might happen in another thread.
+ * Use #GWeakRef if thread-safety is required.
  */
 void
 g_object_weak_ref (GObject    *object,
@@ -2554,7 +2559,7 @@ g_object_weak_unref (GObject    *object,
  * Note that as with g_object_weak_ref(), the weak references created by
  * this method are not thread-safe: they cannot safely be used in one
  * thread if the object's last g_object_unref() might happen in another
- * thread.
+ * thread. Use #GWeakRef if thread-safety is required.
  */
 void
 g_object_add_weak_pointer (GObject  *object, 
@@ -2919,7 +2924,49 @@ g_object_unref (gpointer _object)
     }
   else
     {
-      /* we are about tp remove the last reference */
+      GSList **weak_locations;
+
+      /* The only way that this object can live at this point is if
+       * there are outstanding weak references already established
+       * before we got here.
+       *
+       * If there were not already weak references then no more can be
+       * established at this time, because the other thread would have
+       * to hold a strong ref in order to call
+       * g_object_add_weak_pointer() and then we wouldn't be here.
+       */
+      weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
+
+      if (weak_locations != NULL)
+        {
+          g_rw_lock_writer_lock (&weak_locations_lock);
+
+          /* It is possible that one of the weak references beat us to
+           * the lock. Make sure the refcount is still what we expected
+           * it to be.
+           */
+          old_ref = g_atomic_int_get (&object->ref_count);
+          if (old_ref != 1)
+            {
+              g_rw_lock_writer_unlock (&weak_locations_lock);
+              goto retry_atomic_decrement1;
+            }
+
+          /* We got the lock first, so the object will definitely die
+           * now. Clear out all the weak references.
+           */
+          while (*weak_locations)
+            {
+              GWeakRef *weak_ref_location = (*weak_locations)->data;
+
+              weak_ref_location->priv.p = NULL;
+              *weak_locations = g_slist_delete_link (*weak_locations, *weak_locations);
+            }
+
+          g_rw_lock_writer_unlock (&weak_locations_lock);
+        }
+
+      /* 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);
       TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 1));
@@ -3747,3 +3794,191 @@ static void
 g_initially_unowned_class_init (GInitiallyUnownedClass *klass)
 {
 }
+
+/**
+ * GWeakRef:
+ *
+ * A structure containing a weak reference to a #GObject.  It can either
+ * be empty (i.e. point to %NULL), or point to an object for as long as
+ * at least one "strong" reference to that object exists. Before the
+ * object's #GObjectClass.dispose method is called, every #GWeakRef
+ * associated with becomes empty (i.e. points to %NULL).
+ *
+ * Like #GValue, #GWeakRef can be statically allocated, stack- or
+ * heap-allocated, or embedded in larger structures.
+ *
+ * Unlike g_object_weak_ref() and g_object_add_weak_pointer(), this weak
+ * reference is thread-safe: converting a weak pointer to a reference is
+ * atomic with respect to invalidation of weak pointers to destroyed
+ * objects.
+ *
+ * If the object's #GObjectClass.dispose method results in additional
+ * references to the object being held, any #GWeakRef<!-- -->s taken
+ * before it was disposed will continue to point to %NULL.  If
+ * #GWeakRef<!-- -->s are taken after the object is disposed and
+ * re-referenced, they will continue to point to it until its refcount
+ * goes back to zero, at which point they too will be invalidated.
+ */
+
+/**
+ * g_weak_ref_init: (skip)
+ * @weak_ref_location: (inout): uninitialized or empty location for a weak
+ *  reference
+ * @object: (allow-none): a #GObject or %NULL
+ *
+ * Initialise a non-statically-allocated #GWeakRef.
+ *
+ * This function also calls g_weak_ref_set() with @object on the
+ * freshly-initialised weak reference.
+ *
+ * This function should always be matched with a call to
+ * g_weak_ref_clear().  It is not necessary to use this function for a
+ * #GWeakRef in static storage because it will already be
+ * properly initialised.  Just use g_weak_ref_set() directly.
+ *
+ * Since: 2.32
+ */
+void
+g_weak_ref_init (GWeakRef *weak_ref_location,
+                 gpointer  object)
+{
+  weak_ref_location->priv.p = NULL;
+
+  g_weak_ref_set (weak_ref_location, object);
+}
+
+/**
+ * g_weak_ref_clear: (skip)
+ * @weak_ref_location: (inout): location of a weak reference, which
+ *  may be empty
+ *
+ * Frees resources associated with a non-statically-allocated #GWeakRef.
+ * After this call, the #GWeakRef is left in an undefined state.
+ *
+ * You should only call this on a #GWeakRef that previously had
+ * g_weak_ref_init() called on it.
+ *
+ * Since: 2.32
+ */
+void
+g_weak_ref_clear (GWeakRef *weak_ref_location)
+{
+  g_weak_ref_set (weak_ref_location, NULL);
+
+  /* be unkind */
+  weak_ref_location->priv.p = (void *) 0xccccccccu;
+}
+
+/**
+ * g_weak_ref_get: (skip)
+ * @weak_ref_location: (inout): location of a weak reference to a #GObject
+ *
+ * If @weak_ref_location is not empty, atomically acquire a strong
+ * reference to the object it points to, and return that reference.
+ *
+ * This function is needed because of the potential race between taking
+ * the pointer value and g_object_ref() on it, if the object was losing
+ * its last reference at the same time in a different thread.
+ *
+ * The caller should release the resulting reference in the usual way,
+ * by using g_object_unref().
+ *
+ * Returns: (transfer full) (type GObject.Object): the object pointed to
+ *  by @weak_ref_location, or %NULL if it was empty
+ *
+ * Since: 2.32
+ */
+gpointer
+g_weak_ref_get (GWeakRef *weak_ref_location)
+{
+  gpointer object_or_null;
+
+  g_return_val_if_fail (weak_ref_location != NULL, NULL);
+
+  g_rw_lock_reader_lock (&weak_locations_lock);
+
+  object_or_null = weak_ref_location->priv.p;
+
+  if (object_or_null != NULL)
+    g_object_ref (object_or_null);
+
+  g_rw_lock_reader_unlock (&weak_locations_lock);
+
+  return object_or_null;
+}
+
+/**
+ * g_weak_ref_set: (skip)
+ * @weak_ref_location: location for a weak reference
+ * @object: (allow-none): a #GObject or %NULL
+ *
+ * Change the object to which @weak_ref_location points, or set it to
+ * %NULL.
+ *
+ * You must own a strong reference on @object while calling this
+ * function.
+ *
+ * Since: 2.32
+ */
+void
+g_weak_ref_set (GWeakRef *weak_ref_location,
+                gpointer  object)
+{
+  GSList **weak_locations;
+  GObject *new_object;
+  GObject *old_object;
+
+  g_return_if_fail (weak_ref_location != NULL);
+  g_return_if_fail (object == NULL || G_IS_OBJECT (object));
+
+  new_object = object;
+
+  g_rw_lock_writer_lock (&weak_locations_lock);
+
+  /* We use the extra level of indirection here so that if we have ever
+   * had a weak pointer installed at any point in time on this object,
+   * we can see that there is a non-NULL value associated with the
+   * weak-pointer quark and know that this value will not change at any
+   * point in the object's lifetime.
+   *
+   * Both properties are important for reducing the amount of times we
+   * need to acquire locks and for decreasing the duration of time the
+   * lock is held while avoiding some rather tricky races.
+   *
+   * Specifically: we can avoid having to do an extra unconditional lock
+   * in g_object_unref() without worrying about some extremely tricky
+   * races.
+   */
+
+  old_object = weak_ref_location->priv.p;
+  if (new_object != old_object)
+    {
+      weak_ref_location->priv.p = new_object;
+
+      /* Remove the weak ref from the old object */
+      if (old_object != NULL)
+        {
+          weak_locations = g_datalist_id_get_data (&old_object->qdata, quark_weak_locations);
+          /* for it to point to an object, the object must have had it added once */
+          g_assert (weak_locations != NULL);
+
+          *weak_locations = g_slist_remove (*weak_locations, weak_ref_location);
+        }
+
+      /* Add the weak ref to the new object */
+      if (new_object != NULL)
+        {
+          weak_locations = g_datalist_id_get_data (&new_object->qdata, quark_weak_locations);
+
+          if (weak_locations == NULL)
+            {
+              weak_locations = g_new0 (GSList *, 1);
+              g_datalist_id_set_data_full (&new_object->qdata, quark_weak_locations, weak_locations, g_free);
+            }
+
+          *weak_locations = g_slist_prepend (*weak_locations, weak_ref_location);
+        }
+    }
+
+  g_rw_lock_writer_unlock (&weak_locations_lock);
+}
diff --git a/gobject/gobject.h b/gobject/gobject.h
index ea3b048..3572736 100644
--- a/gobject/gobject.h
+++ b/gobject/gobject.h
@@ -575,6 +575,18 @@ void    g_clear_object (volatile GObject **object_ptr);
       g_object_unref (_o);                                                   \
   } G_STMT_END
 
+typedef struct {
+    /*<private>*/
+    union { gpointer p; } priv;
+} GWeakRef;
+
+void     g_weak_ref_init       (GWeakRef *weak_ref,
+                                gpointer  object);
+void     g_weak_ref_clear      (GWeakRef *weak_ref);
+gpointer g_weak_ref_get        (GWeakRef *weak_ref);
+void     g_weak_ref_set        (GWeakRef *weak_ref,
+                                gpointer  object);
+
 G_END_DECLS
 
 #endif /* __G_OBJECT_H__ */
diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols
index dda51d0..1a7bd57 100644
--- a/gobject/gobject.symbols
+++ b/gobject/gobject.symbols
@@ -385,3 +385,7 @@ g_param_spec_types
 g_slist_remove_all PRIVATE
 g_unichar_validate PRIVATE
 #endif
+g_weak_ref_init
+g_weak_ref_clear
+g_weak_ref_get
+g_weak_ref_set



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