mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-24 22:46:15 +01:00
gobject: fix deadlock with g_weak_ref_set()
In general, we must not call out to external, unknown code while holding a lock. That is prone to dead lock. g_object_ref() can emit a toggle notification. In g_weak_ref_set(), we must not do that while holding the lock.
This commit is contained in:
parent
b397ef2122
commit
e05623001b
@ -3751,33 +3751,25 @@ g_object_remove_toggle_ref (GObject *object,
|
||||
g_critical ("%s: couldn't find toggle ref %p(%p)", G_STRFUNC, notify, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_ref:
|
||||
* @object: (type GObject.Object): a #GObject
|
||||
*
|
||||
* Increases the reference count of @object.
|
||||
*
|
||||
* Since GLib 2.56, if `GLIB_VERSION_MAX_ALLOWED` is 2.56 or greater, the type
|
||||
* of @object will be propagated to the return type (using the GCC typeof()
|
||||
* extension), so any casting the caller needs to do on the return type must be
|
||||
* explicit.
|
||||
*
|
||||
* Returns: (type GObject.Object) (transfer none): the same @object
|
||||
*/
|
||||
gpointer
|
||||
(g_object_ref) (gpointer _object)
|
||||
/* Internal implementation of g_object_ref() which doesn't call out to user code.
|
||||
* @out_toggle_notify and @out_toggle_data *must* be provided, and if non-`NULL`
|
||||
* values are returned, then the caller *must* call that toggle notify function
|
||||
* as soon as it is safe to do so. It may call (or be) user-provided code so should
|
||||
* only be called once all locks are released. */
|
||||
static gpointer
|
||||
object_ref (GObject *object,
|
||||
GToggleNotify *out_toggle_notify,
|
||||
gpointer *out_toggle_data)
|
||||
{
|
||||
GObject *object = _object;
|
||||
GToggleNotify toggle_notify;
|
||||
gpointer toggle_data;
|
||||
gint old_ref;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
old_ref = g_atomic_int_get (&object->ref_count);
|
||||
|
||||
retry:
|
||||
toggle_notify = NULL;
|
||||
toggle_data = NULL;
|
||||
if (old_ref > 1 && old_ref < G_MAXINT)
|
||||
{
|
||||
/* Fast-path. We have apparently more than 1 references already. No
|
||||
@ -3803,12 +3795,43 @@ retry:
|
||||
{
|
||||
gboolean object_already_finalized = TRUE;
|
||||
|
||||
*out_toggle_notify = NULL;
|
||||
*out_toggle_data = NULL;
|
||||
g_return_val_if_fail (!object_already_finalized, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TRACE (GOBJECT_OBJECT_REF (object, G_TYPE_FROM_INSTANCE (object), old_ref));
|
||||
|
||||
*out_toggle_notify = toggle_notify;
|
||||
*out_toggle_data = toggle_data;
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* g_object_ref:
|
||||
* @object: (type GObject.Object): a #GObject
|
||||
*
|
||||
* Increases the reference count of @object.
|
||||
*
|
||||
* Since GLib 2.56, if `GLIB_VERSION_MAX_ALLOWED` is 2.56 or greater, the type
|
||||
* of @object will be propagated to the return type (using the GCC typeof()
|
||||
* extension), so any casting the caller needs to do on the return type must be
|
||||
* explicit.
|
||||
*
|
||||
* Returns: (type GObject.Object) (transfer none): the same @object
|
||||
*/
|
||||
gpointer
|
||||
(g_object_ref) (gpointer _object)
|
||||
{
|
||||
GObject *object = _object;
|
||||
GToggleNotify toggle_notify;
|
||||
gpointer toggle_data;
|
||||
|
||||
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
|
||||
|
||||
object = object_ref (object, &toggle_notify, &toggle_data);
|
||||
|
||||
if (toggle_notify)
|
||||
toggle_notify (toggle_data, object, FALSE);
|
||||
|
||||
@ -5113,20 +5136,25 @@ g_weak_ref_clear (GWeakRef *weak_ref)
|
||||
gpointer
|
||||
g_weak_ref_get (GWeakRef *weak_ref)
|
||||
{
|
||||
gpointer object_or_null;
|
||||
GToggleNotify toggle_notify = NULL;
|
||||
gpointer toggle_data = NULL;
|
||||
GObject *object;
|
||||
|
||||
g_return_val_if_fail (weak_ref!= NULL, NULL);
|
||||
g_return_val_if_fail (weak_ref, NULL);
|
||||
|
||||
g_rw_lock_reader_lock (&weak_locations_lock);
|
||||
|
||||
object_or_null = weak_ref->priv.p;
|
||||
object = weak_ref->priv.p;
|
||||
|
||||
if (object_or_null != NULL)
|
||||
g_object_ref (object_or_null);
|
||||
if (object)
|
||||
object = object_ref (object, &toggle_notify, &toggle_data);
|
||||
|
||||
g_rw_lock_reader_unlock (&weak_locations_lock);
|
||||
|
||||
return object_or_null;
|
||||
if (toggle_notify)
|
||||
toggle_notify (toggle_data, object, FALSE);
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -731,11 +731,9 @@ weak_ref_in_toggle_notify_toggle_cb (gpointer data,
|
||||
return;
|
||||
|
||||
/* We just got a second ref, while calling g_weak_ref_get().
|
||||
* At this point, we hold a lock for the weak ref.
|
||||
*
|
||||
* FIXME: currently we would dead lock with the lines below.
|
||||
* Test that taking another weak ref in this situation works.
|
||||
*/
|
||||
return;
|
||||
|
||||
g_weak_ref_init (&weak2, object);
|
||||
g_assert_true (object == g_weak_ref_get (&weak2));
|
||||
|
Loading…
Reference in New Issue
Block a user