From 71cd9031186ac2aaa11a701ba17c4e1d582a1669 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 20 Aug 2024 12:58:59 +0100 Subject: [PATCH 1/2] tests: Run expected-to-hang cancellable tests in subprocesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests are expected to cause a thread to deadlock. That seems to be fine with glibc on Linux, but the glibc version on FreeBSD can detect the deadlock, and aborts the whole test process with: ``` GLib (gthread-posix.c): Unexpected error from C library during 'pthread_mutex_lock': Resource deadlock avoided. Aborting. ``` This is fair enough. To avoid this causing the test suite to fail, run those two tests in subprocesses. This also means we’re not carrying a deadlocked thread around for the rest of the test suite. Improves on commit 62192925b6. Signed-off-by: Philip Withnall --- gio/tests/cancellable.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index cdba51a02..1698f5072 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -536,6 +536,17 @@ test_cancellable_disconnect_on_cancelled_callback_hangs (void) return; } + /* Run the test in a subprocess. While we can get away with deadlocking a + * specific thread on Linux, the glibc on FreeBSD manages to detect the + * deadlock and aborts the whole test process. */ + if (!g_test_subprocess ()) + { + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + if (!g_test_trap_has_passed ()) + g_test_trap_assert_stderr ("*Unexpected error from C library during 'pthread_mutex_lock': Resource deadlock avoided. Aborting.*"); + return; + } + cancellable = g_cancellable_new (); thread_data.cancellable = cancellable; thread_data.callback = G_CALLBACK (on_cancellable_connect_disconnect); @@ -552,10 +563,9 @@ test_cancellable_disconnect_on_cancelled_callback_hangs (void) thread_loop = thread_data.loop; g_assert_cmpuint ((gulong) (guintptr) 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. + /* 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 until the test subprocess exits. */ cancelling_thread = g_thread_new ("/cancellable/disconnect-on-cancelled-callback-hangs", (GThreadFunc) g_cancellable_cancel, @@ -621,6 +631,17 @@ test_cancellable_reset_on_cancelled_callback_hangs (void) return; } + /* Run the test in a subprocess. While we can get away with deadlocking a + * specific thread on Linux, the glibc on FreeBSD manages to detect the + * deadlock and aborts the whole test process. */ + if (!g_test_subprocess ()) + { + g_test_trap_subprocess (NULL, 0, G_TEST_SUBPROCESS_DEFAULT); + if (!g_test_trap_has_passed ()) + g_test_trap_assert_stderr ("*Unexpected error from C library during 'pthread_mutex_lock': Resource deadlock avoided. Aborting.*"); + return; + } + cancellable = g_cancellable_new (); thread_data.cancellable = cancellable; thread_data.callback = G_CALLBACK (on_cancelled_reset); @@ -637,10 +658,9 @@ test_cancellable_reset_on_cancelled_callback_hangs (void) thread_loop = thread_data.loop; g_assert_cmpuint ((gulong) (guintptr) 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. + /* 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 until the test subprocess exits. */ cancelling_thread = g_thread_new ("/cancellable/reset-on-cancelled-callback-hangs", (GThreadFunc) g_cancellable_cancel, From ce71da63ba566702978ee8f7f290821e073c2f57 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 20 Aug 2024 13:01:17 +0100 Subject: [PATCH 2/2] Revert "gio/tests/cancellable: Explain failure on GCancellableSource tests on valgrind" This reverts commit 365411ea327a5f126896b106fe81a113bc0cc7df. Since commit 3a07b2abd4006da33cdfb0916b266a21070a47d6, issue 2309 has been fixed, so we should no longer have failures from valgrind here. --- gio/tests/cancellable.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gio/tests/cancellable.c b/gio/tests/cancellable.c index 1698f5072..5379812d7 100644 --- a/gio/tests/cancellable.c +++ b/gio/tests/cancellable.c @@ -25,7 +25,6 @@ #include #include "glib/glib-private.h" -#include "glib/gvalgrind.h" /* How long to wait in ms for each iteration */ #define WAIT_ITERATION (10) @@ -276,11 +275,6 @@ test_cancellable_source_threaded_dispose (void) g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2309"); #endif -#ifdef ENABLE_VALGRIND - if (RUNNING_ON_VALGRIND) - g_test_incomplete ("FIXME: Leaks lots of GCancellableSource objects, see glib#2309"); -#endif - /* Create a new thread and wait until it’s ready to execute. Each iteration of * the test will pass it a new #GCancellableSource. */ g_cond_init (&data.cond);