mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-13 21:06:17 +01:00
Merge branch 'safer-toggle-notify' into 'main'
gobject: Ensure an object has toggle references before notifying it Closes #2394 See merge request GNOME/glib!2072
This commit is contained in:
commit
b2b7feda8d
@ -3276,6 +3276,16 @@ toggle_refs_notify (GObject *object,
|
|||||||
ToggleRefStack tstack, *tstackptr;
|
ToggleRefStack tstack, *tstackptr;
|
||||||
|
|
||||||
G_LOCK (toggle_refs_mutex);
|
G_LOCK (toggle_refs_mutex);
|
||||||
|
/* If another thread removed the toggle reference on the object, while
|
||||||
|
* we were waiting here, there's nothing to notify.
|
||||||
|
* So let's check again if the object has toggle reference and in case return.
|
||||||
|
*/
|
||||||
|
if (!OBJECT_HAS_TOGGLE_REF (object))
|
||||||
|
{
|
||||||
|
G_UNLOCK (toggle_refs_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tstackptr = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
tstackptr = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
|
||||||
tstack = *tstackptr;
|
tstack = *tstackptr;
|
||||||
G_UNLOCK (toggle_refs_mutex);
|
G_UNLOCK (toggle_refs_mutex);
|
||||||
|
@ -422,6 +422,91 @@ test_threaded_weak_ref_finalization (void)
|
|||||||
g_assert_null (g_weak_ref_get (&weak));
|
g_assert_null (g_weak_ref_get (&weak));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GObject *object;
|
||||||
|
int done; /* (atomic) */
|
||||||
|
int toggles; /* (atomic) */
|
||||||
|
} ToggleNotifyThreadData;
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
on_reffer_thread (gpointer user_data)
|
||||||
|
{
|
||||||
|
ToggleNotifyThreadData *thread_data = user_data;
|
||||||
|
|
||||||
|
while (!g_atomic_int_get (&thread_data->done))
|
||||||
|
{
|
||||||
|
g_object_ref (thread_data->object);
|
||||||
|
g_object_unref (thread_data->object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_toggle_notify (gpointer data,
|
||||||
|
GObject *object,
|
||||||
|
gboolean is_last_ref)
|
||||||
|
{
|
||||||
|
/* Anything could be put here, but we don't care for this test.
|
||||||
|
* Actually having this empty made the bug to happen more frequently (being
|
||||||
|
* timing related).
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
on_toggler_thread (gpointer user_data)
|
||||||
|
{
|
||||||
|
ToggleNotifyThreadData *thread_data = user_data;
|
||||||
|
|
||||||
|
while (!g_atomic_int_get (&thread_data->done))
|
||||||
|
{
|
||||||
|
g_object_ref (thread_data->object);
|
||||||
|
g_object_remove_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
|
||||||
|
g_object_add_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
|
||||||
|
g_object_unref (thread_data->object);
|
||||||
|
g_atomic_int_add (&thread_data->toggles, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_threaded_toggle_notify (void)
|
||||||
|
{
|
||||||
|
GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||||
|
ToggleNotifyThreadData data = { object, FALSE, 0 };
|
||||||
|
GThread *threads[3];
|
||||||
|
gsize i;
|
||||||
|
|
||||||
|
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2394");
|
||||||
|
g_test_summary ("Test that toggle reference notifications can be changed "
|
||||||
|
"safely from another (the main) thread without causing the "
|
||||||
|
"notifying thread to abort");
|
||||||
|
|
||||||
|
g_object_add_toggle_ref (object, on_toggle_notify, &data);
|
||||||
|
g_object_unref (object);
|
||||||
|
|
||||||
|
g_assert_cmpint (object->ref_count, ==, 1);
|
||||||
|
threads[0] = g_thread_new ("on_reffer_thread", on_reffer_thread, &data);
|
||||||
|
threads[1] = g_thread_new ("on_another_reffer_thread", on_reffer_thread, &data);
|
||||||
|
threads[2] = g_thread_new ("on_main_toggler_thread", on_toggler_thread, &data);
|
||||||
|
|
||||||
|
/* We need to wait here for the threads to run for a bit in order to make the
|
||||||
|
* race to happen, so we wait for an high number of toggle changes to be met
|
||||||
|
* so that we can be consistent on each platform.
|
||||||
|
*/
|
||||||
|
while (g_atomic_int_get (&data.toggles) < 1000000)
|
||||||
|
;
|
||||||
|
g_atomic_int_set (&data.done, TRUE);
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (threads); i++)
|
||||||
|
g_thread_join (threads[i]);
|
||||||
|
|
||||||
|
g_assert_cmpint (object->ref_count, ==, 1);
|
||||||
|
g_clear_object (&object);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc,
|
main (int argc,
|
||||||
char *argv[])
|
char *argv[])
|
||||||
@ -433,6 +518,8 @@ main (int argc,
|
|||||||
g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
|
g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
|
||||||
g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
|
g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
|
||||||
test_threaded_weak_ref_finalization);
|
test_threaded_weak_ref_finalization);
|
||||||
|
g_test_add_func ("/GObject/threaded-toggle-notify",
|
||||||
|
test_threaded_toggle_notify);
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user