mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
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:
parent
28c87a5594
commit
46c2f570da
@ -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);
|
||||||
|
}
|
||||||
|
@ -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__ */
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user