From 62192925b6ea3597043d7771be58ba7a21a8407b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 20 Jun 2022 20:53:26 +0200 Subject: [PATCH 1/9] gio/tests: Add tests for cancellable pollfd and cancellation callbacks --- gio/tests/cancellable.c | 244 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index 278d2752e..c51d13f29 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -338,6 +338,246 @@ test_cancellable_source_threaded_dispose (void) #endif } +static void +test_cancellable_poll_fd (void) +{ + GCancellable *cancellable; + GPollFD pollfd = {.fd = -1}; + int fd = -1; + +#ifdef G_OS_WIN32 + g_test_skip ("Platform not supported"); + return; +#endif + + cancellable = g_cancellable_new (); + + g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd)); + g_assert_cmpint (pollfd.fd, >, 0); + + fd = g_cancellable_get_fd (cancellable); + g_assert_cmpint (fd, >, 0); + + g_cancellable_release_fd (cancellable); + g_cancellable_release_fd (cancellable); + + g_object_unref (cancellable); +} + +static void +test_cancellable_cancelled_poll_fd (void) +{ + GCancellable *cancellable; + GPollFD pollfd; + +#ifdef G_OS_WIN32 + g_test_skip ("Platform not supported"); + return; +#endif + + g_test_summary ("Tests that cancellation wakes up a pollable FD on creation"); + + cancellable = g_cancellable_new (); + g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd)); + g_cancellable_cancel (cancellable); + + g_poll (&pollfd, 1, -1); + + g_cancellable_release_fd (cancellable); + g_object_unref (cancellable); +} + +typedef struct { + GCancellable *cancellable; + gboolean polling_started; /* Atomic */ +} CancellablePollThreadData; + +static gpointer +cancel_cancellable_thread (gpointer user_data) +{ + CancellablePollThreadData *thread_data = user_data; + + while (!g_atomic_int_get (&thread_data->polling_started)) + ; + + /* Let's just wait a moment before cancelling, this is not really needed + * but we do it to simulate that the thread is actually doing something. + */ + g_usleep (G_USEC_PER_SEC / 10); + g_cancellable_cancel (thread_data->cancellable); + + return NULL; +} + +static gpointer +polling_cancelled_cancellable_thread (gpointer user_data) +{ + CancellablePollThreadData *thread_data = user_data; + GPollFD pollfd; + + g_assert_true (g_cancellable_make_pollfd (thread_data->cancellable, &pollfd)); + g_atomic_int_set (&thread_data->polling_started, TRUE); + + g_poll (&pollfd, 1, -1); + + g_cancellable_release_fd (thread_data->cancellable); + + return NULL; +} + +static void +test_cancellable_cancelled_poll_fd_threaded (void) +{ + GCancellable *cancellable; + CancellablePollThreadData thread_data = {0}; + GThread *polling_thread = NULL; + GThread *cancelling_thread = NULL; + GPollFD pollfd; + +#ifdef G_OS_WIN32 + g_test_skip ("Platform not supported"); + return; +#endif + + g_test_summary ("Tests that a cancellation wakes up a pollable FD"); + + cancellable = g_cancellable_new (); + g_assert_true (g_cancellable_make_pollfd (cancellable, &pollfd)); + + thread_data.cancellable = cancellable; + + polling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/polling", + polling_cancelled_cancellable_thread, + &thread_data); + cancelling_thread = g_thread_new ("/cancellable/poll-fd-cancelled-threaded/cancelling", + cancel_cancellable_thread, &thread_data); + + g_poll (&pollfd, 1, -1); + g_assert_true (g_cancellable_is_cancelled (cancellable)); + g_cancellable_release_fd (cancellable); + + g_thread_join (g_steal_pointer (&cancelling_thread)); + g_thread_join (g_steal_pointer (&polling_thread)); + + g_object_unref (cancellable); +} + +typedef struct { + GMainLoop *loop; + GCancellable *cancellable; + GCallback callback; + gboolean is_disconnecting; + gulong handler_id; +} ConnectingThreadData; + +static void +on_cancellable_connect_disconnect (GCancellable *cancellable, + ConnectingThreadData *data) +{ + gulong handler_id = (gulong) g_atomic_pointer_exchange (&data->handler_id, 0); + g_atomic_int_set (&data->is_disconnecting, TRUE); + g_cancellable_disconnect (cancellable, handler_id); + g_atomic_int_set (&data->is_disconnecting, FALSE); +} + +static gpointer +connecting_thread (gpointer user_data) +{ + GMainContext *context; + ConnectingThreadData *data = user_data; + gulong handler_id; + GMainLoop *loop; + + handler_id = + g_cancellable_connect (data->cancellable, data->callback, data, NULL); + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + loop = g_main_loop_new (context, FALSE); + + g_atomic_pointer_set (&data->handler_id, handler_id); + g_atomic_pointer_set (&data->loop, loop); + g_main_loop_run (loop); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); + g_main_loop_unref (loop); + + return NULL; +} + +static void +test_cancellable_disconnect_on_cancelled_callback_hangs (void) +{ + GCancellable *cancellable; + GThread *thread = NULL; + GThread *cancelling_thread = NULL; + ConnectingThreadData thread_data = {0}; + GMainLoop *thread_loop; + gpointer waited; + + /* While this is not convenient, it's done to ensure that we don't have a + * race when trying to cancelling a cancellable that is about to be cancelled + * in another thread + */ + g_test_summary ("Tests that trying to disconnect a cancellable from the " + "cancelled signal callback will result in a deadlock " + "as per #GCancellable::cancelled"); + + if (!g_test_undefined ()) + { + g_test_skip ("Skipping testing disallowed behaviour of disconnecting from " + "a cancellable from its cancelled callback"); + return; + } + + cancellable = g_cancellable_new (); + thread_data.cancellable = cancellable; + thread_data.callback = G_CALLBACK (on_cancellable_connect_disconnect); + + g_assert_false (g_atomic_int_get (&thread_data.is_disconnecting)); + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), ==, 0); + + thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs", + connecting_thread, &thread_data); + + while (!g_atomic_pointer_get (&thread_data.loop)) + ; + + thread_loop = thread_data.loop; + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), !=, 0); + + /* FIXME: This thread will hang (at least that's what this test wants to + * ensure), but we can't stop it from the caller, unless we'll expose + * pthread_cancel (and similar) to GLib. + * So it will keep hanging till the test process is alive. + */ + cancelling_thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs", + (GThreadFunc) g_cancellable_cancel, + cancellable); + + while (!g_cancellable_is_cancelled (cancellable) || + !g_atomic_int_get (&thread_data.is_disconnecting)) + ; + + g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting)); + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), ==, 0); + + waited = &waited; + g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited); + while (waited != NULL) + g_main_context_iteration (NULL, TRUE); + + g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting)); + + g_main_loop_quit (thread_loop); + g_assert_true (g_atomic_int_get (&thread_data.is_disconnecting)); + + g_thread_join (g_steal_pointer (&thread)); + g_thread_unref (cancelling_thread); + g_object_unref (cancellable); +} + int main (int argc, char *argv[]) { @@ -345,6 +585,10 @@ main (int argc, char *argv[]) g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent); g_test_add_func ("/cancellable/null", test_cancel_null); + g_test_add_func ("/cancellable/disconnect-on-cancelled-callback-hangs", test_cancellable_disconnect_on_cancelled_callback_hangs); + g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd); + g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd); + g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded); g_test_add_func ("/cancellable-source/threaded-dispose", test_cancellable_source_threaded_dispose); return g_test_run (); From e218371f1906500437b12f641b86517e5c427cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jun 2022 00:37:22 +0200 Subject: [PATCH 2/9] gio/tests: Ensure that cancellable is cancelled when emitting the signal Use a race between threads resetting and cancelling a cancellable and ensure that when we call the callback the cancellable is cancelled --- gio/tests/cancellable.c | 65 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index c51d13f29..d27cd7c74 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -578,6 +578,70 @@ test_cancellable_disconnect_on_cancelled_callback_hangs (void) g_object_unref (cancellable); } +static gpointer +repeatedly_cancelling_thread (gpointer data) +{ + GCancellable *cancellable = data; + const guint iterations = 10000; + + for (guint i = 0; i < iterations; ++i) + g_cancellable_cancel (cancellable); + + return NULL; +} + +static gpointer +repeatedly_resetting_thread (gpointer data) +{ + GCancellable *cancellable = data; + const guint iterations = 10000; + + for (guint i = 0; i < iterations; ++i) + g_cancellable_reset (cancellable); + + return NULL; +} + +static void +on_racy_cancellable_cancelled (GCancellable *cancellable, + gpointer data) +{ + gboolean *callback_called = data; + + g_assert_true (g_cancellable_is_cancelled (cancellable)); + g_atomic_int_set (callback_called, TRUE); +} + +static void +test_cancellable_cancel_reset_races (void) +{ + GCancellable *cancellable; + GThread *resetting_thread = NULL; + GThread *cancelling_thread = NULL; + gboolean callback_called = FALSE; + + g_test_summary ("Tests threads racing for cancelling and resetting a GCancellable"); + + cancellable = g_cancellable_new (); + + g_cancellable_connect (cancellable, G_CALLBACK (on_racy_cancellable_cancelled), + &callback_called, NULL); + g_assert_false (callback_called); + + resetting_thread = g_thread_new ("/cancellable/cancel-reset-races/resetting", + repeatedly_resetting_thread, + cancellable); + cancelling_thread = g_thread_new ("/cancellable/cancel-reset-races/cancelling", + repeatedly_cancelling_thread, cancellable); + + g_thread_join (g_steal_pointer (&cancelling_thread)); + g_thread_join (g_steal_pointer (&resetting_thread)); + + g_assert_true (callback_called); + + g_object_unref (cancellable); +} + int main (int argc, char *argv[]) { @@ -589,6 +653,7 @@ main (int argc, char *argv[]) g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd); g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd); g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded); + g_test_add_func ("/cancellable/cancel-reset-races", test_cancellable_cancel_reset_races); g_test_add_func ("/cancellable-source/threaded-dispose", test_cancellable_source_threaded_dispose); return g_test_run (); From e7269a26e447c3fab85e823079d000d298cb0931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 22 Jun 2022 03:44:20 +0200 Subject: [PATCH 3/9] gio/tests: Ensure that a cancellable hangs if reset from cancellable callback --- gio/tests/cancellable.c | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index d27cd7c74..5d8bf8a85 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -467,6 +467,7 @@ typedef struct { GCancellable *cancellable; GCallback callback; gboolean is_disconnecting; + gboolean is_resetting; gulong handler_id; } ConnectingThreadData; @@ -578,6 +579,91 @@ test_cancellable_disconnect_on_cancelled_callback_hangs (void) g_object_unref (cancellable); } +static void +on_cancelled_reset (GCancellable *cancellable, + gpointer data) +{ + ConnectingThreadData *thread_data = data; + + g_assert_true (g_cancellable_is_cancelled (cancellable)); + g_atomic_int_set (&thread_data->is_resetting, TRUE); + g_cancellable_reset (cancellable); + g_assert_false (g_cancellable_is_cancelled (cancellable)); + g_atomic_int_set (&thread_data->is_resetting, TRUE); +} + +static void +test_cancellable_reset_on_cancelled_callback_hangs (void) +{ + GCancellable *cancellable; + GThread *thread = NULL; + GThread *cancelling_thread = NULL; + ConnectingThreadData thread_data = {0}; + GMainLoop *thread_loop; + gpointer waited; + + /* While this is not convenient, it's done to ensure that we don't have a + * race when trying to cancelling a cancellable that is about to be cancelled + * in another thread + */ + g_test_summary ("Tests that trying to reset a cancellable from the " + "cancelled signal callback will result in a deadlock " + "as per #GCancellable::cancelled"); + + if (!g_test_undefined ()) + { + g_test_skip ("Skipping testing disallowed behaviour of resetting a " + "cancellable from its callback"); + return; + } + + cancellable = g_cancellable_new (); + thread_data.cancellable = cancellable; + thread_data.callback = G_CALLBACK (on_cancelled_reset); + + g_assert_false (g_atomic_int_get (&thread_data.is_resetting)); + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), ==, 0); + + thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs", + connecting_thread, &thread_data); + + while (!g_atomic_pointer_get (&thread_data.loop)) + ; + + thread_loop = thread_data.loop; + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), !=, 0); + + /* FIXME: This thread will hang (at least that's what this test wants to + * ensure), but we can't stop it from the caller, unless we'll expose + * pthread_cancel (and similar) to GLib. + * So it will keep hanging till the test process is alive. + */ + cancelling_thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs", + (GThreadFunc) g_cancellable_cancel, + cancellable); + + while (!g_cancellable_is_cancelled (cancellable) || + !g_atomic_int_get (&thread_data.is_resetting)) + ; + + g_assert_true (g_atomic_int_get (&thread_data.is_resetting)); + g_assert_cmpuint ((gulong) g_atomic_pointer_get (&thread_data.handler_id), >, 0); + + waited = &waited; + g_timeout_add_once (100, (GSourceOnceFunc) g_nullify_pointer, &waited); + while (waited != NULL) + g_main_context_iteration (NULL, TRUE); + + g_assert_true (g_atomic_int_get (&thread_data.is_resetting)); + + g_main_loop_quit (thread_loop); + g_assert_true (g_atomic_int_get (&thread_data.is_resetting)); + + g_thread_join (g_steal_pointer (&thread)); + g_thread_unref (cancelling_thread); + g_object_unref (cancellable); +} + static gpointer repeatedly_cancelling_thread (gpointer data) { @@ -650,6 +736,7 @@ main (int argc, char *argv[]) g_test_add_func ("/cancellable/multiple-concurrent", test_cancel_multiple_concurrent); g_test_add_func ("/cancellable/null", test_cancel_null); g_test_add_func ("/cancellable/disconnect-on-cancelled-callback-hangs", test_cancellable_disconnect_on_cancelled_callback_hangs); + g_test_add_func ("/cancellable/resets-on-cancel-callback-hangs", test_cancellable_reset_on_cancelled_callback_hangs); g_test_add_func ("/cancellable/poll-fd", test_cancellable_poll_fd); g_test_add_func ("/cancellable/poll-fd-cancelled", test_cancellable_cancelled_poll_fd); g_test_add_func ("/cancellable/poll-fd-cancelled-threaded", test_cancellable_cancelled_poll_fd_threaded); From 920f54e795b1577a3270c6322f547e0963b2d32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 20 Jun 2022 18:37:25 +0200 Subject: [PATCH 4/9] gatomicarray: Use atomic exchange for data value We can use pointer exchange now to avoid doing two operations to switch to the new data pointer. Since we're asserting in case of invalid data, we can just do this check at later point, without involving any different behavior. This changes in the unlikely case that G_DISABLE_ASSERT is defined, as in such case we should undo the operation. --- gobject/gatomicarray.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gobject/gatomicarray.c b/gobject/gatomicarray.c index 43111e8c7..88a07f929 100644 --- a/gobject/gatomicarray.c +++ b/gobject/gatomicarray.c @@ -161,11 +161,18 @@ _g_atomic_array_update (GAtomicArray *array, guint8 *old; G_LOCK (array); - old = g_atomic_pointer_get (&array->data); + old = g_atomic_pointer_exchange (&array->data, new_data); +#ifdef G_DISABLE_ASSERT + if (old && G_ATOMIC_ARRAY_DATA_SIZE (new_data) < G_ATOMIC_ARRAY_DATA_SIZE (old)) + { + g_atomic_pointer_set (&array->data, old); + g_return_if_reached (); + } +#else g_assert (old == NULL || G_ATOMIC_ARRAY_DATA_SIZE (old) <= G_ATOMIC_ARRAY_DATA_SIZE (new_data)); +#endif - g_atomic_pointer_set (&array->data, new_data); if (old) freelist_free (old); G_UNLOCK (array); From 576e5f2f875125c4ee06505790da22a2878c9198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 20 Jun 2022 18:57:36 +0200 Subject: [PATCH 5/9] cancellable: Use more atomic exchanges operations We used to do get and set atomic operations pair, but these may be unsafe in some cases as threads may rely on data that is changed in in between them, however this is not a problem if we do exchange the pointers. So just use exchange ops, in this way we can avoid lock/unlock mutex dances --- gio/gcancellable.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gio/gcancellable.c b/gio/gcancellable.c index 64755206b..13a891a69 100644 --- a/gio/gcancellable.c +++ b/gio/gcancellable.c @@ -273,12 +273,10 @@ g_cancellable_reset (GCancellable *cancellable) g_cond_wait (&cancellable_cond, &cancellable_mutex); } - if (g_atomic_int_get (&priv->cancelled)) + if (g_atomic_int_exchange (&priv->cancelled, FALSE)) { if (priv->wakeup) GLIB_PRIVATE_CALL (g_wakeup_acknowledge) (priv->wakeup); - - g_atomic_int_set (&priv->cancelled, FALSE); } g_mutex_unlock (&cancellable_mutex); @@ -497,13 +495,12 @@ g_cancellable_cancel (GCancellable *cancellable) g_mutex_lock (&cancellable_mutex); - if (g_atomic_int_get (&priv->cancelled)) + if (g_atomic_int_exchange (&priv->cancelled, TRUE)) { g_mutex_unlock (&cancellable_mutex); return; } - g_atomic_int_set (&priv->cancelled, TRUE); priv->cancelled_running = TRUE; if (priv->wakeup) From 9c32cfbaaa42a89d4d69d62dccd931cf065c53ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 21 Jun 2022 04:59:54 +0200 Subject: [PATCH 6/9] gfileattribute: Do atomic addition before checking the old value on ref So we avoid working on a value that is not been updated yet. --- gio/gfileattribute.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gio/gfileattribute.c b/gio/gfileattribute.c index aa8c61e45..124eb4d07 100644 --- a/gio/gfileattribute.c +++ b/gio/gfileattribute.c @@ -859,11 +859,12 @@ GFileAttributeInfoList * g_file_attribute_info_list_ref (GFileAttributeInfoList *list) { GFileAttributeInfoListPriv *priv = (GFileAttributeInfoListPriv *)list; + int old_ref_count; g_return_val_if_fail (list != NULL, NULL); - g_return_val_if_fail (priv->ref_count > 0, NULL); - g_atomic_int_inc (&priv->ref_count); + old_ref_count = g_atomic_int_add (&priv->ref_count, 1); + g_return_val_if_fail (old_ref_count > 0, NULL); return list; } From bfd77693cedc37713b6f1799a694fd44af9ccfe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 21 Jun 2022 05:01:17 +0200 Subject: [PATCH 7/9] gresource: Use atomic pointer exchange operations to nullify and check --- gio/gresource.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gio/gresource.c b/gio/gresource.c index 44d517d12..4ccd33364 100644 --- a/gio/gresource.c +++ b/gio/gresource.c @@ -1443,14 +1443,13 @@ g_static_resource_fini (GStaticResource *static_resource) register_lazy_static_resources_unlocked (); - resource = g_atomic_pointer_get (&static_resource->resource); + resource = g_atomic_pointer_exchange (&static_resource->resource, NULL); if (resource) { /* There should be at least two references to the resource now: one for * static_resource->resource, and one in the registered_resources list. */ g_assert (g_atomic_int_get (&resource->ref_count) >= 2); - g_atomic_pointer_set (&static_resource->resource, NULL); g_resources_unregister_unlocked (resource); g_resource_unref (resource); } From 2c322f2a650bc60dcbb254746352af420d515d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 21 Jun 2022 05:01:55 +0200 Subject: [PATCH 8/9] gmain: Do atomic addition before checking the old value on ref So we avoid working on a value that is not been updated yet. --- glib/gmain.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/glib/gmain.c b/glib/gmain.c index a0ade8acb..6571fa239 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -551,10 +551,12 @@ GSourceFuncs g_idle_funcs = GMainContext * g_main_context_ref (GMainContext *context) { - g_return_val_if_fail (context != NULL, NULL); - g_return_val_if_fail (g_atomic_int_get (&context->ref_count) > 0, NULL); + int old_ref_count; - g_atomic_int_inc (&context->ref_count); + g_return_val_if_fail (context != NULL, NULL); + + old_ref_count = g_atomic_int_add (&context->ref_count, 1); + g_return_val_if_fail (old_ref_count > 0, NULL); return context; } From 00f3f0d407da0cdfceed3342a51147e725ab3cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Tue, 21 Jun 2022 05:02:30 +0200 Subject: [PATCH 9/9] gthread: Use atomic pointer exchange to check value set on g_init_leave --- glib/gthread.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/glib/gthread.c b/glib/gthread.c index 0b03e6408..bc9b1c2b3 100644 --- a/glib/gthread.c +++ b/glib/gthread.c @@ -741,11 +741,13 @@ void gsize result) { gsize *value_location = (gsize *) location; + gsize old_value; - g_return_if_fail (g_atomic_pointer_get (value_location) == 0); g_return_if_fail (result != 0); - g_atomic_pointer_set (value_location, result); + old_value = (gsize) g_atomic_pointer_exchange (value_location, result); + g_return_if_fail (old_value == 0); + g_mutex_lock (&g_once_mutex); g_return_if_fail (g_once_init_list != NULL); g_once_init_list = g_slist_remove (g_once_init_list, (void*) value_location);