mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-08-26 19:08:57 +02:00
gobject: Cleanup GWeakRef locations on object finalization
It can happen that a GWeakRef is added to an object while it's disposing (or even during finalizing) and this may happen in a thread that (weak) references an object while the disposal isn't completed yet or when using toggle references and switching to GWeakRef on notification (as the API suggests). In such scenario the weak locations are not cleaned up when the object is finalized, and will point to a free'd area. So, during finalization and when we're sure that the object will be destroyed for sure, check again if there are new weak locations and unset them if any as part of the qdata destruction. Do this adding a new utility function so that we can avoid duplicating code to free the weak locations. Added various tests simulating this case. Fixes: #2390
This commit is contained in:
@@ -233,6 +233,7 @@ static guint object_floating_flag_handler (GObject *object,
|
||||
|
||||
static void object_interface_check_properties (gpointer check_data,
|
||||
gpointer g_iface);
|
||||
static void weak_locations_free_unlocked (GSList **weak_locations);
|
||||
|
||||
/* --- typedefs --- */
|
||||
typedef struct _GObjectNotifyQueue GObjectNotifyQueue;
|
||||
@@ -3512,6 +3513,10 @@ g_object_unref (gpointer _object)
|
||||
* 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.
|
||||
*
|
||||
* Other GWeakRef's (weak locations) instead may still be added
|
||||
* before the object is finalized, but in such case we'll unset
|
||||
* them as part of the qdata removal.
|
||||
*/
|
||||
weak_locations = g_datalist_id_get_data (&object->qdata, quark_weak_locations);
|
||||
|
||||
@@ -3533,13 +3538,8 @@ g_object_unref (gpointer _object)
|
||||
/* 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);
|
||||
}
|
||||
weak_locations_free_unlocked (weak_locations);
|
||||
g_datalist_id_remove_no_notify (&object->qdata, quark_weak_locations);
|
||||
|
||||
g_rw_lock_writer_unlock (&weak_locations_lock);
|
||||
}
|
||||
@@ -4646,6 +4646,35 @@ g_weak_ref_get (GWeakRef *weak_ref)
|
||||
return object_or_null;
|
||||
}
|
||||
|
||||
static void
|
||||
weak_locations_free_unlocked (GSList **weak_locations)
|
||||
{
|
||||
if (*weak_locations)
|
||||
{
|
||||
GSList *weak_location;
|
||||
|
||||
for (weak_location = *weak_locations; weak_location;)
|
||||
{
|
||||
GWeakRef *weak_ref_location = weak_location->data;
|
||||
|
||||
weak_ref_location->priv.p = NULL;
|
||||
weak_location = g_slist_delete_link (weak_location, weak_location);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (weak_locations);
|
||||
}
|
||||
|
||||
static void
|
||||
weak_locations_free (gpointer data)
|
||||
{
|
||||
GSList **weak_locations = data;
|
||||
|
||||
g_rw_lock_writer_lock (&weak_locations_lock);
|
||||
weak_locations_free_unlocked (weak_locations);
|
||||
g_rw_lock_writer_unlock (&weak_locations_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* g_weak_ref_set: (skip)
|
||||
* @weak_ref: location for a weak reference
|
||||
@@ -4712,7 +4741,8 @@ g_weak_ref_set (GWeakRef *weak_ref,
|
||||
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);
|
||||
g_datalist_id_set_data_full (&new_object->qdata, quark_weak_locations,
|
||||
weak_locations, weak_locations_free);
|
||||
}
|
||||
|
||||
*weak_locations = g_slist_prepend (*weak_locations, weak_ref);
|
||||
|
Reference in New Issue
Block a user