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