gobject: Do not call toggle down notifications if current refcount is not 1

When an object is revitalized and a notify callbacks increased the reference
counter of the object, we are calling the toggle notifier twice, while it
should only happen if also the actual reference count value is 1 (after
having been decremented from 2).
This commit is contained in:
Marco Trevisan (Treviño) 2022-12-06 04:27:53 +01:00
parent 1f852863ec
commit 0918ce013a
2 changed files with 42 additions and 2 deletions

View File

@ -3891,7 +3891,8 @@ g_object_unref (gpointer _object)
TRACE (GOBJECT_OBJECT_UNREF(object,G_TYPE_FROM_INSTANCE(object),old_ref));
/* if we went from 2->1 we need to notify toggle refs if any */
if (old_ref == 2 && OBJECT_HAS_TOGGLE_REF (object))
if (old_ref == 2 && OBJECT_HAS_TOGGLE_REF (object) &&
g_atomic_int_get ((int *)&object->ref_count) == 1)
{
/* The last ref being held in this case is owned by the toggle_ref */
toggle_refs_notify (object, TRUE);

View File

@ -731,6 +731,11 @@ toggle_notify (gpointer data,
g_assert (is_last == c->should_be_last);
if (is_last)
g_assert_cmpint (g_atomic_int_get (&obj->ref_count), ==, 1);
else
g_assert_cmpint (g_atomic_int_get (&obj->ref_count), ==, 2);
c->count++;
}
@ -1021,6 +1026,11 @@ toggle_notify_counter (gpointer data,
{
Count *c = data;
c->count++;
if (is_last)
g_assert_cmpint (g_atomic_int_get (&obj->ref_count), ==, 1);
else
g_assert_cmpint (g_atomic_int_get (&obj->ref_count), ==, 2);
}
static void
@ -1049,6 +1059,20 @@ on_object_notify_switch_to_toggle_ref (GObject *object,
g_object_unref (object);
}
static void
on_object_notify_add_ref (GObject *object,
GParamSpec *pspec,
void *data)
{
DisposeReffingObject *obj = DISPOSE_REFFING_OBJECT (object);
int old_toggle_cout = obj->actual.count;
obj->notify_called++;
g_object_ref (object);
g_assert_cmpint (obj->actual.count, ==, old_toggle_cout);
}
static void
test_toggle_ref_and_notify_on_dispose (void)
{
@ -1116,8 +1140,23 @@ test_toggle_ref_and_notify_on_dispose (void)
disposed_checker = &obj;
g_object_add_weak_pointer (G_OBJECT (obj), &disposed_checker);
obj->disposing_refs = 0;
/* Check that notification is triggered after being queued, but that toggle
* is not happening if current refcount changed.
*/
obj->disposing_refs = 1;
obj->disposing_refs_all_normal = FALSE;
obj->expected.count = 7;
obj->notify_handler = G_CALLBACK (on_object_notify_add_ref);
g_object_remove_toggle_ref (G_OBJECT (obj), obj->toggle_notify, NULL);
g_assert_cmpint (obj->actual.count, ==, 8);
g_assert_cmpuint (obj->notify_called, ==, 4);
g_object_unref (obj);
disposed_checker = &obj;
g_object_add_weak_pointer (G_OBJECT (obj), &disposed_checker);
obj->disposing_refs = 0;
obj->expected.count = 9;
g_clear_object (&obj);
g_assert_null (disposed_checker);
}