From 46c2f570da10c93d7b2fab790048f957bb422302 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Tue, 6 Dec 2011 12:27:07 +0000 Subject: [PATCH] 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 a8d4f9e2c..ad75a30f1 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, 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 #GWeakRefs taken + * before it was disposed will continue to point to %NULL. If + * #GWeakRefs 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 ea3b048ab..35727365d 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 { + /**/ + 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 dda51d07b..1a7bd57ff 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