[PATCH v2] Add threaded variants of single-threaded malloc tests
Arjun Shankar
arjun@redhat.com
Mon Nov 10 14:44:22 GMT 2025
Single-threaded malloc tests exercise only the SINGLE_THREAD_P paths in
the malloc implementation. This commit runs variants of these tests in
a multi-threaded environment in order to exercise the alternate code
paths in the same test scenarios, thus potentially improving coverage.
$(test)-threaded-main and $(test)-threaded-worker variants are
introduced for most single-threaded malloc tests (with a small number of
exceptions). The -main variants run the base test in a main thread
while the test environment has an alternate thread running, whereas the
-worker variants run the test in an alternate thread while the main
thread waits on it.
Test support/ infrastructure is extended to be able to allow threaded
execution of these tests without needing to modify the tests themselves,
using a compile-time -D flag to enable threaded execution instead.
Tests that for any reason currently fail are excluded. They should be
investigated and followed-up on.
---
* v1: https://inbox.sourceware.org/libc-alpha/20251104172806.3931431-1-arjun@redhat.com/
* Changes in v2: addressed Florian's comment:
- Added missed dependencies to address Hurd testsuite build failures
* For clarity, these are the lines that came from an LLM (and I slightly
edited them after):
> $(objpfx)%-threaded-main.o: CPPFLAGS += -DTEST_IN_THREAD=THREAD_MAIN
> $(objpfx)%-threaded-main.o: %.c $(before-compile)
> $(compile-command.c)
> $(objpfx)%-threaded-worker.o: CPPFLAGS += -DTEST_IN_THREAD=THREAD_WORKER
> $(objpfx)%-threaded-worker.o: %.c $(before-compile)
> $(compile-command.c)
---
malloc/Makefile | 53 ++++++++++++++
support/Makefile | 1 +
support/support_test_in_thread_wrapper.c | 90 ++++++++++++++++++++++++
support/support_test_main.c | 25 +++++--
support/test-driver.c | 8 +++
support/test-driver.h | 10 +++
6 files changed, 181 insertions(+), 6 deletions(-)
create mode 100644 support/support_test_in_thread_wrapper.c
diff --git a/malloc/Makefile b/malloc/Makefile
index cc012e2921..a2d547477f 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -293,10 +293,63 @@ $(addprefix $(objpfx), $(tests-link-with-libpthread)): $(shared-thread-library)
$(tests-link-with-libpthread:%=$(objpfx)%-malloc-largetcache): \
$(shared-thread-library)
+# tests-threaded-{main,worker} are variants of base malloc tests that are
+# run in a multi-threaded program. The main variant is run in the main
+# thread while an alternate thread exists, and the worker variant is run in
+# a worker thread while the main thread waits on it.
+
+tests-exclude-threaded = \
+ $(tests-link-with-libpthread) \
+ $(tests-static) \
+ tst-compathooks-off \
+ tst-compathooks-on \
+ tst-interpose-nothread \
+ tst-malloc-usable \
+ tst-malloc-usable-tunables \
+ tst-mallocstate \
+ tst-memalign-2 \
+ tst-mxfast \
+ tst-safe-linking \
+# tests-exclude-threaded
+
+tests-threaded-main = $(addsuffix -threaded-main, \
+ $(filter-out $(tests-exclude-threaded), \
+ $(tests)))
+$(addprefix $(objpfx), $(tests-threaded-main)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-main:%=%-malloc-check)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-main:%=%-malloc-hugetlb1)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-main:%=%-malloc-hugetlb2)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-main:%=%-mcheck)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-main:%=%-malloc-largetcache)): $(shared-thread-library)
+
+tests-threaded-worker = $(addsuffix -threaded-worker, \
+ $(filter-out $(tests-exclude-threaded) \
+ tst-free-errno \
+ tst-mallocfork, \
+ $(tests)))
+$(addprefix $(objpfx), $(tests-threaded-worker)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-worker:%=%-malloc-check)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-worker:%=%-malloc-hugetlb1)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-worker:%=%-malloc-hugetlb2)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-worker:%=%-mcheck)): $(shared-thread-library)
+$(addprefix $(objpfx), $(tests-threaded-worker:%=%-malloc-largetcache)): $(shared-thread-library)
+
+tests += $(tests-threaded-main) $(tests-threaded-worker)
+
# These should be removed by `make clean'.
extra-objs = mcheck-init.o libmcheck.a
others-extras = mcheck-init.o
+# Compile threaded-{main,worker} tests from their corresponding base
+# sources, but with -DTEST_IN_THREAD appropriately defined so that support
+# infrastructure runs them in a multi-threaded program.
+$(objpfx)%-threaded-main.o: CPPFLAGS += -DTEST_IN_THREAD=THREAD_MAIN
+$(objpfx)%-threaded-main.o: %.c $(before-compile)
+ $(compile-command.c)
+$(objpfx)%-threaded-worker.o: CPPFLAGS += -DTEST_IN_THREAD=THREAD_WORKER
+$(objpfx)%-threaded-worker.o: %.c $(before-compile)
+ $(compile-command.c)
+
# Include the cleanup handler.
aux := set-freeres thread-freeres
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..a4d5df0c12
--- /dev/null
+++ b/support/support_test_in_thread_wrapper.c
@@ -0,0 +1,90 @@
+/* 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
+ <https://www.gnu.org/licenses/>. */
+
+#include <support/test-driver.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <support/xthread.h>
+
+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;
+}
+
+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 == 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 == 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..2d76ba4aab 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. */
+ THREAD_MAIN = 1,
+ THREAD_WORKER = 2,
};
/* Options provided by the test driver. */
@@ -78,6 +85,9 @@ extern unsigned int test_verbose;
printf (__VA_ARGS__); \
} while (0);
+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
--
2.51.0
More information about the Libc-alpha
mailing list