diff --git a/support/Makefile b/support/Makefile
index 2043e4e590..a7b1b39782 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -98,6 +98,7 @@ libsupport-routines = \
support_test_compare_failure \
support_test_compare_string \
support_test_compare_string_wide \
+ support_test_in_thread_wrapper \
support_test_main \
support_test_verify_impl \
support_wait_for_thread_exit \
diff --git a/support/support_test_in_thread_wrapper.c b/support/support_test_in_thread_wrapper.c
new file mode 100644
index 0000000000..56f1d74437
--- /dev/null
+++ b/support/support_test_in_thread_wrapper.c
@@ -0,0 +1,96 @@
+/* Test-in-thread wrapper function for the test driver.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C 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 Public
+ License along with the GNU C Library; if not, see
+ .
+
+ The support_test_in_thread_wrapper function defined here gets called from
+ within support_test_main, but needs to be compiled separately. This is
+ done in order to avoid linking every test (irrespective of necessity)
+ against pthread_create etc., which has implications for Hurd and
+ statically linked tests. */
+
+#include
+#include
+#include
+#include
+
+struct test_thread_args
+{
+ int argc;
+ char **argv;
+ const struct test_config *config;
+ int ret;
+};
+
+static void *
+test_thread (void *closure)
+{
+ struct test_thread_args *args = closure;
+
+ if (args->config->test_function != NULL)
+ args->ret = args->config->test_function ();
+ else if (args->config->test_function_argv != NULL)
+ args->ret = args->config->test_function_argv (args->argc, args->argv);
+ else
+ {
+ printf ("error: no test function defined\n");
+ exit (1);
+ }
+ return NULL;
+}
+
+static pthread_barrier_t barrier;
+
+static void *
+empty_thread (void *closure)
+{
+ /* Make sure that the alternate thread waits (and exists) till the main
+ thread finishes running the test. */
+ xpthread_barrier_wait (&barrier);
+
+ return NULL;
+}
+
+int
+support_test_in_thread_wrapper (int argc, char **argv,
+ const struct test_config *config)
+{
+ pthread_t thread;
+ struct test_thread_args closure = {.argc = argc,
+ .argv = argv,
+ .config = config};
+
+ if (config->test_in_thread == TEST_THREAD_MAIN)
+ {
+ xpthread_barrier_init (&barrier, NULL, 2);
+ thread = xpthread_create (NULL, empty_thread, NULL);
+
+ /* Run the test in the main thread. */
+ test_thread (&closure);
+
+ /* Signal to the alternate thread that it can return. */
+ xpthread_barrier_wait (&barrier);
+ xpthread_join (thread);
+ }
+ else /* config->test_in_thread == TEST_THREAD_WORKER. */
+ {
+ /* Run the test in an alternate thread. */
+ thread = xpthread_create (NULL, test_thread, &closure);
+ xpthread_join (thread);
+ }
+
+ return closure.ret;
+}
diff --git a/support/support_test_main.c b/support/support_test_main.c
index 1558e27c57..baca343cda 100644
--- a/support/support_test_main.c
+++ b/support/support_test_main.c
@@ -238,14 +238,27 @@ run_test_function (int argc, char **argv, const struct test_config *config)
exit (1);
}
- if (config->test_function != NULL)
- return config->test_function ();
- else if (config->test_function_argv != NULL)
- return config->test_function_argv (argc, argv);
+ if (config->test_in_thread == 0)
+ {
+ if (config->test_function != NULL)
+ return config->test_function ();
+ else if (config->test_function_argv != NULL)
+ return config->test_function_argv (argc, argv);
+ else
+ {
+ printf ("error: no test function defined\n");
+ exit (1);
+ }
+ }
else
{
- printf ("error: no test function defined\n");
- exit (1);
+ if (config->test_in_thread_wrapper != NULL)
+ return config->test_in_thread_wrapper (argc, argv, config);
+ else
+ {
+ printf ("error: no test-in-thread wrapper defined\n");
+ exit (1);
+ }
}
}
diff --git a/support/test-driver.c b/support/test-driver.c
index 40e2bc0ee2..cb65b9f078 100644
--- a/support/test-driver.c
+++ b/support/test-driver.c
@@ -168,5 +168,13 @@ main (int argc, char **argv)
test_config.optstring = "+";
#endif
+#ifdef TEST_IN_THREAD
+ test_config.test_in_thread = TEST_IN_THREAD;
+ test_config.test_in_thread_wrapper = support_test_in_thread_wrapper;
+#else
+ test_config.test_in_thread = 0;
+ test_config.test_in_thread_wrapper = NULL;
+#endif
+
return support_test_main (argc, argv, &test_config);
}
diff --git a/support/test-driver.h b/support/test-driver.h
index 9291611fbf..19fc601363 100644
--- a/support/test-driver.h
+++ b/support/test-driver.h
@@ -38,6 +38,8 @@ struct test_config
char no_setvbuf; /* Boolean flag to disable setvbuf. */
char run_command_mode; /* Boolean flag to indicate run-command-mode. */
const char *optstring; /* Short command line options. */
+ int test_in_thread; /* 0 => no threading, MAIN, WORKER. */
+ int (*test_in_thread_wrapper) (int, char **, const struct test_config *);
};
enum
@@ -54,6 +56,11 @@ enum
/* Used for command line argument parsing. */
OPT_DIRECT = 1000,
OPT_TESTDIR,
+
+ /* Used for TEST_IN_THREAD to run single-threaded tests in a
+ multi-threaded environment. */
+ TEST_THREAD_MAIN = 1,
+ TEST_THREAD_WORKER = 2,
};
/* Options provided by the test driver. */
@@ -78,6 +85,11 @@ extern unsigned int test_verbose;
printf (__VA_ARGS__); \
} while (0);
+/* Tests that define -DTEST_IN_THREAD are run in a thread while an alternate
+ thread exists and waits on it. */
+extern int support_test_in_thread_wrapper (int argc, char **argv,
+ const struct test_config *config);
+
int support_test_main (int argc, char **argv, const struct test_config *);
__END_DECLS