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
This commit is contained in:
Ryan Lortie 2011-12-06 12:27:07 +00:00
parent 28c87a5594
commit 46c2f570da
3 changed files with 253 additions and 2 deletions

View File

@ -200,6 +200,9 @@ static gulong gobject_signals[LAST_SIGNAL] = { 0, };
static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler; static guint (*floating_flag_handler) (GObject*, gint) = object_floating_flag_handler;
G_LOCK_DEFINE_STATIC (construction_mutex); G_LOCK_DEFINE_STATIC (construction_mutex);
static GSList *construction_objects = NULL; 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); 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_closure_array = g_quark_from_static_string ("GObject-closure-array");
quark_weak_refs = g_quark_from_static_string ("GObject-weak-references"); 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_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue"); quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
pspec_pool = g_param_spec_pool_new (TRUE); 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 * Note that the weak references created by this method are not
* thread-safe: they cannot safely be used in one thread if the * thread-safe: they cannot safely be used in one thread if the
* object's last g_object_unref() might happen in another thread. * object's last g_object_unref() might happen in another thread.
* Use #GWeakRef if thread-safety is required.
*/ */
void void
g_object_weak_ref (GObject *object, 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 * 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 * 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 if the object's last g_object_unref() might happen in another
* thread. * thread. Use #GWeakRef if thread-safety is required.
*/ */
void void
g_object_add_weak_pointer (GObject *object, g_object_add_weak_pointer (GObject *object,
@ -2919,7 +2924,49 @@ g_object_unref (gpointer _object)
} }
else 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)); TRACE (GOBJECT_OBJECT_DISPOSE(object,G_TYPE_FROM_INSTANCE(object), 1));
G_OBJECT_GET_CLASS (object)->dispose (object); G_OBJECT_GET_CLASS (object)->dispose (object);
TRACE (GOBJECT_OBJECT_DISPOSE_END(object,G_TYPE_FROM_INSTANCE(object), 1)); 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) 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);
}

View File

@ -575,6 +575,18 @@ void g_clear_object (volatile GObject **object_ptr);
g_object_unref (_o); \ g_object_unref (_o); \
} G_STMT_END } 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 G_END_DECLS
#endif /* __G_OBJECT_H__ */ #endif /* __G_OBJECT_H__ */

View File

@ -385,3 +385,7 @@ g_param_spec_types
g_slist_remove_all PRIVATE g_slist_remove_all PRIVATE
g_unichar_validate PRIVATE g_unichar_validate PRIVATE
#endif #endif
g_weak_ref_init
g_weak_ref_clear
g_weak_ref_get
g_weak_ref_set