2005-05-05 14:57:29 +00:00
|
|
|
/* GObject - GLib Type, Object, Parameter and Signal Library
|
|
|
|
* Copyright (C) 2005 Red Hat, Inc.
|
|
|
|
*
|
2022-06-01 12:17:28 +01:00
|
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
*
|
2005-05-05 14:57:29 +00:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
2017-05-28 14:09:39 +02:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2005-05-05 14:57:29 +00:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General
|
2014-01-23 12:58:29 +01:00
|
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2005-05-05 14:57:29 +00:00
|
|
|
*/
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
#include <glib-object.h>
|
2005-05-05 14:57:29 +00:00
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* This test tests weak and toggle references */
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
static GObject *global_object;
|
|
|
|
|
|
|
|
static gboolean object_destroyed;
|
|
|
|
static gboolean weak_ref1_notified;
|
|
|
|
static gboolean weak_ref2_notified;
|
|
|
|
static gboolean toggle_ref1_weakened;
|
|
|
|
static gboolean toggle_ref1_strengthened;
|
|
|
|
static gboolean toggle_ref2_weakened;
|
|
|
|
static gboolean toggle_ref2_strengthened;
|
|
|
|
static gboolean toggle_ref3_weakened;
|
|
|
|
static gboolean toggle_ref3_strengthened;
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* TestObject, a parent class for TestObject */
|
2012-11-02 15:19:20 +00:00
|
|
|
static GType test_object_get_type (void);
|
2005-05-05 14:57:29 +00:00
|
|
|
#define TEST_TYPE_OBJECT (test_object_get_type ())
|
|
|
|
typedef struct _TestObject TestObject;
|
|
|
|
typedef struct _TestObjectClass TestObjectClass;
|
|
|
|
|
|
|
|
struct _TestObject
|
|
|
|
{
|
|
|
|
GObject parent_instance;
|
|
|
|
};
|
|
|
|
struct _TestObjectClass
|
|
|
|
{
|
|
|
|
GObjectClass parent_class;
|
|
|
|
};
|
|
|
|
|
2014-10-17 11:54:02 +01:00
|
|
|
G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT)
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
test_object_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
object_destroyed = TRUE;
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
G_OBJECT_CLASS (test_object_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_object_class_init (TestObjectClass *class)
|
|
|
|
{
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
|
|
|
|
object_class->finalize = test_object_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_object_init (TestObject *test_object)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clear_flags (void)
|
|
|
|
{
|
|
|
|
object_destroyed = FALSE;
|
|
|
|
weak_ref1_notified = FALSE;
|
|
|
|
weak_ref2_notified = FALSE;
|
|
|
|
toggle_ref1_weakened = FALSE;
|
|
|
|
toggle_ref1_strengthened = FALSE;
|
|
|
|
toggle_ref2_weakened = FALSE;
|
|
|
|
toggle_ref2_strengthened = FALSE;
|
|
|
|
toggle_ref3_weakened = FALSE;
|
|
|
|
toggle_ref3_strengthened = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weak_ref1 (gpointer data,
|
2022-03-13 17:45:50 +01:00
|
|
|
GObject *object)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (object == global_object);
|
|
|
|
g_assert_cmpint (GPOINTER_TO_INT (data), ==, 42);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
weak_ref1_notified = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weak_ref2 (gpointer data,
|
2022-03-13 17:45:50 +01:00
|
|
|
GObject *object)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (object == global_object);
|
|
|
|
g_assert_cmpint (GPOINTER_TO_INT (data), ==, 24);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
weak_ref2_notified = TRUE;
|
|
|
|
}
|
|
|
|
|
2024-01-31 12:11:07 +01:00
|
|
|
static void
|
|
|
|
weak_ref3 (gpointer data,
|
|
|
|
GObject *object)
|
|
|
|
{
|
|
|
|
GWeakRef *weak_ref = data;
|
|
|
|
|
|
|
|
g_assert_null (g_weak_ref_get (weak_ref));
|
|
|
|
|
|
|
|
weak_ref2_notified = TRUE;
|
|
|
|
}
|
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
static void
|
|
|
|
toggle_ref1 (gpointer data,
|
2022-03-13 17:45:50 +01:00
|
|
|
GObject *object,
|
|
|
|
gboolean is_last_ref)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (object == global_object);
|
|
|
|
g_assert_cmpint (GPOINTER_TO_INT (data), ==, 42);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
if (is_last_ref)
|
|
|
|
toggle_ref1_weakened = TRUE;
|
|
|
|
else
|
|
|
|
toggle_ref1_strengthened = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
toggle_ref2 (gpointer data,
|
2022-03-13 17:45:50 +01:00
|
|
|
GObject *object,
|
|
|
|
gboolean is_last_ref)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (object == global_object);
|
|
|
|
g_assert_cmpint (GPOINTER_TO_INT (data), ==, 24);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
if (is_last_ref)
|
|
|
|
toggle_ref2_weakened = TRUE;
|
|
|
|
else
|
|
|
|
toggle_ref2_strengthened = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
toggle_ref3 (gpointer data,
|
2022-03-13 17:45:50 +01:00
|
|
|
GObject *object,
|
|
|
|
gboolean is_last_ref)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (object == global_object);
|
|
|
|
g_assert_cmpint (GPOINTER_TO_INT (data), ==, 34);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
if (is_last_ref)
|
|
|
|
{
|
|
|
|
toggle_ref3_weakened = TRUE;
|
|
|
|
g_object_remove_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
toggle_ref3_strengthened = TRUE;
|
|
|
|
}
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
static void
|
|
|
|
test_references (void)
|
2005-05-05 14:57:29 +00:00
|
|
|
{
|
|
|
|
GObject *object;
|
2024-01-31 12:11:07 +01:00
|
|
|
GWeakRef weak_ref;
|
2005-05-05 14:57:29 +00:00
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* Test basic weak reference operation */
|
2005-05-05 14:57:29 +00:00
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (weak_ref1_notified);
|
|
|
|
g_assert_true (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
/* Test two weak references at once
|
|
|
|
*/
|
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
|
|
|
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (weak_ref1_notified);
|
|
|
|
g_assert_true (weak_ref2_notified);
|
|
|
|
g_assert_true (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* Test remove weak references */
|
2005-05-05 14:57:29 +00:00
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_weak_ref (object, weak_ref1, GUINT_TO_POINTER (42));
|
|
|
|
g_object_weak_ref (object, weak_ref2, GUINT_TO_POINTER (24));
|
|
|
|
g_object_weak_unref (object, weak_ref1, GUINT_TO_POINTER (42));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (weak_ref1_notified);
|
|
|
|
g_assert_true (weak_ref2_notified);
|
|
|
|
g_assert_true (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
2024-01-31 12:11:07 +01:00
|
|
|
/* Test that within a GWeakNotify the GWeakRef is NULL already. */
|
|
|
|
weak_ref2_notified = FALSE;
|
|
|
|
object = g_object_new (G_TYPE_OBJECT, NULL);
|
|
|
|
g_weak_ref_init (&weak_ref, object);
|
|
|
|
g_assert_true (object == g_weak_ref_get (&weak_ref));
|
|
|
|
g_object_weak_ref (object, weak_ref3, &weak_ref);
|
|
|
|
g_object_unref (object);
|
|
|
|
g_object_unref (object);
|
|
|
|
g_assert_true (weak_ref2_notified);
|
|
|
|
g_weak_ref_clear (&weak_ref);
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* Test basic toggle reference operation */
|
2005-05-05 14:57:29 +00:00
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (toggle_ref1_weakened);
|
|
|
|
g_assert_false (toggle_ref1_strengthened);
|
|
|
|
g_assert_false (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_ref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (toggle_ref1_weakened);
|
|
|
|
g_assert_true (toggle_ref1_strengthened);
|
|
|
|
g_assert_false (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
g_object_unref (object);
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (toggle_ref1_weakened);
|
|
|
|
g_assert_false (toggle_ref1_strengthened);
|
|
|
|
g_assert_true (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
/* Test two toggle references at once */
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_add_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
|
|
|
g_object_add_toggle_ref (object, toggle_ref2, GUINT_TO_POINTER (24));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (toggle_ref1_weakened);
|
|
|
|
g_assert_false (toggle_ref1_strengthened);
|
|
|
|
g_assert_false (toggle_ref2_weakened);
|
|
|
|
g_assert_false (toggle_ref2_strengthened);
|
|
|
|
g_assert_false (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_remove_toggle_ref (object, toggle_ref1, GUINT_TO_POINTER (42));
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (toggle_ref1_weakened);
|
|
|
|
g_assert_false (toggle_ref1_strengthened);
|
|
|
|
g_assert_true (toggle_ref2_weakened);
|
|
|
|
g_assert_false (toggle_ref2_strengthened);
|
|
|
|
g_assert_false (object_destroyed);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
|
|
|
clear_flags ();
|
2021-05-27 21:09:45 +05:30
|
|
|
/* Check that removing a toggle ref with %NULL data works fine. */
|
|
|
|
g_object_remove_toggle_ref (object, toggle_ref2, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_false (toggle_ref1_weakened);
|
|
|
|
g_assert_false (toggle_ref1_strengthened);
|
|
|
|
g_assert_false (toggle_ref2_weakened);
|
|
|
|
g_assert_false (toggle_ref2_strengthened);
|
|
|
|
g_assert_true (object_destroyed);
|
|
|
|
|
|
|
|
/* Test a toggle reference that removes itself */
|
2005-05-05 14:57:29 +00:00
|
|
|
global_object = object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
2022-03-13 17:45:50 +01:00
|
|
|
|
2005-05-05 14:57:29 +00:00
|
|
|
g_object_add_toggle_ref (object, toggle_ref3, GUINT_TO_POINTER (34));
|
|
|
|
|
|
|
|
clear_flags ();
|
|
|
|
g_object_unref (object);
|
2022-03-13 17:45:50 +01:00
|
|
|
g_assert_true (toggle_ref3_weakened);
|
|
|
|
g_assert_false (toggle_ref3_strengthened);
|
|
|
|
g_assert_true (object_destroyed);
|
|
|
|
}
|
|
|
|
|
2025-04-09 10:59:53 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static guint weak_ref4_notified;
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
static guint weak_ref4_finalizing_notified;
|
|
|
|
static guint weak_ref4_finalizing_notified_skipped;
|
|
|
|
static gint weak_ref4_finalizing_resurrected = -1;
|
|
|
|
static guint weak_ref4_indexes_n;
|
|
|
|
static const guint *weak_ref4_indexes;
|
2025-04-09 10:59:53 +02:00
|
|
|
|
|
|
|
static void
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
weak_ref4_finalizing (gpointer data,
|
|
|
|
GObject *object)
|
2025-04-09 10:59:53 +02:00
|
|
|
{
|
|
|
|
static gint last = -1;
|
|
|
|
gint idx;
|
|
|
|
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
g_assert_true (weak_ref4_finalizing_resurrected == FALSE || weak_ref4_finalizing_resurrected == 4);
|
|
|
|
|
2025-04-09 07:52:34 +02:00
|
|
|
idx = (gint) GPOINTER_TO_UINT (data);
|
|
|
|
g_assert_cmpint (last, <, idx);
|
|
|
|
last = idx;
|
2025-04-09 10:59:53 +02:00
|
|
|
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
weak_ref4_finalizing_notified++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
weak_ref4 (gpointer data,
|
|
|
|
GObject *object)
|
|
|
|
{
|
|
|
|
static gint last = -1;
|
|
|
|
guint idx;
|
|
|
|
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified, ==, 0);
|
|
|
|
|
|
|
|
idx = GPOINTER_TO_UINT (data);
|
|
|
|
g_assert_cmpint (last, <, (gint) idx);
|
|
|
|
last = (gint) idx;
|
|
|
|
|
2025-04-09 10:59:53 +02:00
|
|
|
weak_ref4_notified++;
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
|
|
|
|
if (weak_ref4_finalizing_resurrected == -1)
|
|
|
|
{
|
|
|
|
if (g_random_boolean ())
|
|
|
|
{
|
|
|
|
/* Resurrect the object. */
|
|
|
|
weak_ref4_finalizing_resurrected = TRUE;
|
|
|
|
g_object_ref (object);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
weak_ref4_finalizing_resurrected = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_weak_ref (object, weak_ref4_finalizing, GUINT_TO_POINTER (idx));
|
|
|
|
|
|
|
|
if (weak_ref4_indexes_n > 0 && (g_random_int () % 4 == 0))
|
|
|
|
{
|
|
|
|
|
|
|
|
while (weak_ref4_indexes_n > 0 && weak_ref4_indexes[weak_ref4_indexes_n - 1] <= idx)
|
|
|
|
{
|
|
|
|
/* already invoked. Skip. */
|
|
|
|
weak_ref4_indexes_n--;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* From the callback, we unregister another callback that we know is
|
|
|
|
* still registered. */
|
|
|
|
if (weak_ref4_indexes_n > 0)
|
|
|
|
{
|
|
|
|
guint new_idx = weak_ref4_indexes[weak_ref4_indexes_n - 1];
|
|
|
|
|
|
|
|
weak_ref4_indexes_n--;
|
|
|
|
g_object_weak_unref (object, weak_ref4, GUINT_TO_POINTER (new_idx));
|
|
|
|
|
|
|
|
/* Behave as if we got this callback invoked still, so that the remainder matches up. */
|
|
|
|
weak_ref4_finalizing_notified_skipped++;
|
|
|
|
weak_ref4_notified++;
|
|
|
|
}
|
|
|
|
}
|
2025-04-09 10:59:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_references_many (void)
|
|
|
|
{
|
|
|
|
GObject *object;
|
|
|
|
guint *indexes;
|
|
|
|
guint i;
|
|
|
|
guint n;
|
|
|
|
guint m;
|
|
|
|
|
|
|
|
/* Test subscribing a (random) number of weak references. */
|
|
|
|
object = g_object_new (TEST_TYPE_OBJECT, NULL);
|
|
|
|
n = g_random_int () % 1000;
|
|
|
|
indexes = g_new (guint, MAX (n, 1));
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
g_object_weak_ref (object, weak_ref4, GUINT_TO_POINTER (i));
|
|
|
|
indexes[i] = i;
|
|
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
guint tmp, j;
|
|
|
|
|
|
|
|
j = (guint) g_random_int_range ((gint) i, (gint) n);
|
|
|
|
tmp = indexes[i];
|
|
|
|
indexes[i] = indexes[j];
|
|
|
|
indexes[j] = tmp;
|
|
|
|
}
|
2025-04-09 07:52:34 +02:00
|
|
|
m = g_random_int () % (n + 1u);
|
|
|
|
for (i = 0; i < m; i++)
|
|
|
|
g_object_weak_unref (object, weak_ref4, GUINT_TO_POINTER (indexes[i]));
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
weak_ref4_indexes_n = n - m;
|
|
|
|
weak_ref4_indexes = &indexes[m];
|
2025-04-09 10:59:53 +02:00
|
|
|
g_object_unref (object);
|
|
|
|
g_assert_cmpint (weak_ref4_notified, ==, n - m);
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
if (n - m == 0)
|
|
|
|
{
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_resurrected, ==, -1);
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified, ==, 0);
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified_skipped, ==, 0);
|
|
|
|
}
|
|
|
|
else if (weak_ref4_finalizing_resurrected == TRUE)
|
|
|
|
{
|
|
|
|
weak_ref4_finalizing_resurrected = 4;
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified, ==, 0);
|
|
|
|
g_object_unref (object);
|
|
|
|
g_assert_cmpint (weak_ref4_notified, ==, n - m);
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified + weak_ref4_finalizing_notified_skipped, ==, n - m);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_resurrected, ==, FALSE);
|
|
|
|
g_assert_cmpint (weak_ref4_finalizing_notified + weak_ref4_finalizing_notified_skipped, ==, n - m);
|
|
|
|
}
|
2025-04-09 10:59:53 +02:00
|
|
|
g_free (indexes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
gobject: invoke g_object_weak_ref() one-by-one during destruction
Previously, at two places (in g_object_real_dispose() and shortly before
finalize()), we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This clears @quark_weak_notifies at once and then invokes all
notifications.
This means, if you were inside a notification callback and called
g_object_weak_unref() on the object for *another* weak-reference, then
an exception failed:
GLib-GObject-FATAL-CRITICAL: g_object_weak_unref_cb: couldn't find weak ref 0x401320(0x16b9fe0)
Granted, maybe inside a GWeakNotify you shouldn't call much of anything
on where_the_object_was. However, unregistering things (like calling
g_object_weak_unref()) should still reasonably work.
Instead, now remove each weak notification one by one and invoke it.
As we now invoke the callbacks in a loop, if a callee registers a new
callback, then that one gets unregistered right away too. Previously,
we would during g_object_real_dispose() only notify the notifications
that were present when the loop starts. This is similar to what happens
in closure_array_destroy_all(). This is a change in behavior, but it
will be fixed in a separate follow-up commit.
https://gitlab.gnome.org/GNOME/glib/-/issues/1002
2025-04-09 11:00:49 +02:00
|
|
|
static void notify_two (gpointer data, GObject *former_object);
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_one (gpointer data, GObject *former_object)
|
|
|
|
{
|
|
|
|
g_object_weak_unref (data, notify_two, former_object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_two (gpointer data, GObject *former_object)
|
|
|
|
{
|
|
|
|
g_assert_not_reached ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_references_two (void)
|
|
|
|
{
|
|
|
|
GObject *obj;
|
|
|
|
|
|
|
|
/* https://gitlab.gnome.org/GNOME/gtk/-/issues/5542#note_1688809 */
|
|
|
|
|
|
|
|
obj = g_object_new (G_TYPE_OBJECT, NULL);
|
|
|
|
|
|
|
|
g_object_weak_ref (obj, notify_one, obj);
|
|
|
|
g_object_weak_ref (obj, notify_two, obj);
|
|
|
|
|
|
|
|
g_object_unref (obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
#define RUN_DISPOSE_N_THREADS 5
|
|
|
|
|
|
|
|
GThread *run_dispose_threads[RUN_DISPOSE_N_THREADS];
|
|
|
|
GObject *run_dispose_obj;
|
|
|
|
gint run_dispose_thread_ready_count;
|
|
|
|
const guint RUN_DISPOSE_N_ITERATIONS = 5000;
|
|
|
|
|
|
|
|
gint run_dispose_weak_notify_pending;
|
|
|
|
gint run_dispose_weak_notify_pending_nested;
|
|
|
|
|
|
|
|
static void
|
|
|
|
_run_dispose_weak_notify_nested (gpointer data,
|
|
|
|
GObject *where_the_object_was)
|
|
|
|
{
|
|
|
|
g_assert_true (run_dispose_obj == where_the_object_was);
|
|
|
|
g_assert_cmpint (g_atomic_int_add (&run_dispose_weak_notify_pending_nested, -1), >, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
_run_dispose_weak_notify (gpointer data,
|
|
|
|
GObject *where_the_object_was)
|
|
|
|
{
|
|
|
|
g_assert_true (run_dispose_obj == where_the_object_was);
|
|
|
|
g_assert_cmpint (g_atomic_int_add (&run_dispose_weak_notify_pending, -1), >, 0);
|
|
|
|
|
|
|
|
if (g_random_int () % 10 == 0)
|
|
|
|
{
|
|
|
|
g_object_run_dispose (run_dispose_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_random_int () % 10 == 0)
|
|
|
|
{
|
|
|
|
g_atomic_int_inc (&run_dispose_weak_notify_pending_nested);
|
|
|
|
g_object_weak_ref (run_dispose_obj, _run_dispose_weak_notify_nested, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static gpointer
|
|
|
|
_run_dispose_thread_fcn (gpointer thread_data)
|
|
|
|
{
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
g_atomic_int_inc (&run_dispose_thread_ready_count);
|
|
|
|
|
|
|
|
while (g_atomic_int_get (&run_dispose_thread_ready_count) != RUN_DISPOSE_N_THREADS)
|
|
|
|
g_usleep (10);
|
|
|
|
|
|
|
|
for (i = 0; i < RUN_DISPOSE_N_ITERATIONS; i++)
|
|
|
|
{
|
|
|
|
g_atomic_int_inc (&run_dispose_weak_notify_pending);
|
|
|
|
g_object_weak_ref (run_dispose_obj, _run_dispose_weak_notify, NULL);
|
|
|
|
if (g_random_int () % 10 == 0)
|
|
|
|
g_object_run_dispose (run_dispose_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_run_dispose (run_dispose_obj);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_references_run_dispose (void)
|
|
|
|
{
|
|
|
|
GQuark quark_weak_notifies = g_quark_from_static_string ("GObject-weak-notifies");
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
run_dispose_obj = g_object_new (G_TYPE_OBJECT, NULL);
|
|
|
|
|
|
|
|
for (i = 0; i < RUN_DISPOSE_N_THREADS; i++)
|
|
|
|
{
|
|
|
|
run_dispose_threads[i] = g_thread_new ("run-dispose", _run_dispose_thread_fcn, GINT_TO_POINTER (i));
|
|
|
|
}
|
|
|
|
for (i = 0; i < RUN_DISPOSE_N_THREADS; i++)
|
|
|
|
{
|
|
|
|
g_thread_join (run_dispose_threads[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_assert_cmpint (g_atomic_int_get (&run_dispose_weak_notify_pending), ==, 0);
|
|
|
|
|
|
|
|
if (g_atomic_int_get (&run_dispose_weak_notify_pending_nested) == 0)
|
|
|
|
{
|
|
|
|
g_assert_null (g_object_get_qdata (run_dispose_obj, quark_weak_notifies));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_assert_nonnull (g_object_get_qdata (run_dispose_obj, quark_weak_notifies));
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_run_dispose (run_dispose_obj);
|
|
|
|
|
|
|
|
g_assert_cmpint (g_atomic_int_get (&run_dispose_weak_notify_pending), ==, 0);
|
|
|
|
g_assert_cmpint (g_atomic_int_get (&run_dispose_weak_notify_pending_nested), ==, 0);
|
|
|
|
g_assert_null (g_object_get_qdata (run_dispose_obj, quark_weak_notifies));
|
|
|
|
|
|
|
|
g_clear_object (&run_dispose_obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
int
|
|
|
|
main (int argc,
|
|
|
|
char *argv[])
|
|
|
|
{
|
|
|
|
g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
|
|
|
|
G_LOG_LEVEL_WARNING |
|
|
|
|
G_LOG_LEVEL_CRITICAL);
|
|
|
|
|
|
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
|
|
|
|
g_test_add_func ("/gobject/references", test_references);
|
2025-04-09 10:59:53 +02:00
|
|
|
g_test_add_func ("/gobject/references-many", test_references_many);
|
gobject: invoke g_object_weak_ref() one-by-one during destruction
Previously, at two places (in g_object_real_dispose() and shortly before
finalize()), we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This clears @quark_weak_notifies at once and then invokes all
notifications.
This means, if you were inside a notification callback and called
g_object_weak_unref() on the object for *another* weak-reference, then
an exception failed:
GLib-GObject-FATAL-CRITICAL: g_object_weak_unref_cb: couldn't find weak ref 0x401320(0x16b9fe0)
Granted, maybe inside a GWeakNotify you shouldn't call much of anything
on where_the_object_was. However, unregistering things (like calling
g_object_weak_unref()) should still reasonably work.
Instead, now remove each weak notification one by one and invoke it.
As we now invoke the callbacks in a loop, if a callee registers a new
callback, then that one gets unregistered right away too. Previously,
we would during g_object_real_dispose() only notify the notifications
that were present when the loop starts. This is similar to what happens
in closure_array_destroy_all(). This is a change in behavior, but it
will be fixed in a separate follow-up commit.
https://gitlab.gnome.org/GNOME/glib/-/issues/1002
2025-04-09 11:00:49 +02:00
|
|
|
g_test_add_func ("/gobject/references_two", test_references_two);
|
gobject: preserve weak notifications registered during dispose
We call g_object_weak_release_all() at two places.
Once right before finalize(). At this point, the object is definitely
going to be destroyed, and the user must no longer resurrect it or
subscribe new weak notifications. In that case, we really want to
notify/release all weak notifications.
However, we also call it from g_object_real_dispose(). During dispose,
the API allows the user to resurrect an object. Granted, that is
probably not something anybody should do, but GObject makes a reasonable
attempt to support that.
A possible place to resurrect (and subscribe new weak notifications) is
when GObject calls g_object_real_dispose().
static void
g_object_real_dispose (GObject *object)
{
g_signal_handlers_destroy (object);
/* GWeakNotify and GClosure can call into user code */
g_object_weak_release_all (object);
closure_array_destroy_all (object);
}
But previously, g_object_weak_release_all() would continue iterating
until there are no more weak notifications left. So while the user can
take a strong reference and resurrect the object, their attempts to
register new weak notifications are thwarted.
Instead, when the loop in g_object_weak_release_all() starts, remember
the initial number of weak notifications, and don't release more than
that. Note that WeakRefStack preserves the order of entries, so by
maintaining the "remaining_to_notify" counter we know when to stop.
Note that this brings also an earlier behavior back, where we would call
g_datalist_id_set_data (&object->qdata, quark_weak_notifies, NULL);
This would take out the entire WeakRefStack at once and notify the weak
notifications registered at the time. But subsequent registrations would
not be released/notified yet.
2025-04-09 08:59:29 +02:00
|
|
|
g_test_add_func ("/gobject/references_run_dispose", test_references_run_dispose);
|
2005-05-05 14:57:29 +00:00
|
|
|
|
2022-03-13 17:45:50 +01:00
|
|
|
return g_test_run ();
|
2005-05-05 14:57:29 +00:00
|
|
|
}
|