From: "Richard W.M. Jones" <rjones(a)redhat.com>
You can now set TEST_C_API_PARALLEL=<N> to run the tests
in parallel (in <N> threads).
The default is *not* to run them in parallel.
In practice this doesn't seem to make that much difference. On my
laptop it's something like 10% faster with 2 threads, and slows down
after that.
Using iostat, I can see that the CPU usage is about 40%, while
/dev/sda usage is pegged to 100% the whole time. Also the workload is
quite write-heavy.
---
generator/actions.ml | 5 +-
tests/c-api/Makefile.am | 1 +
tests/c-api/tests-main.c | 116 +++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 117 insertions(+), 5 deletions(-)
diff --git a/generator/actions.ml b/generator/actions.ml
index 0bb04be..ffe1b33 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -7047,7 +7047,10 @@ file of zeroes, use C<guestfs_fallocate64> instead." };
[["mkfifo"; "0o644"; "/utimens-fifo"];
["utimens"; "/utimens-fifo"; "12345";
"67890"; "9876"; "5432"];
["stat"; "/utimens-fifo"]], "ret->mtime ==
9876");
- InitScratchFS, Always, TestResult (
+ (* Below test fails in parallel. I think it implicitly depends
+ * on one of the other tests.
+ *)
+ InitScratchFS, Disabled, TestResult (
[["ln_sf"; "/utimens-file"; "/utimens-link"];
["utimens"; "/utimens-link"; "12345";
"67890"; "9876"; "5432"];
["stat"; "/utimens-link"]], "ret->mtime ==
9876");
diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am
index d55d76d..102cac8 100644
--- a/tests/c-api/Makefile.am
+++ b/tests/c-api/Makefile.am
@@ -80,6 +80,7 @@ tests_CPPFLAGS = \
-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \
-I$(top_srcdir)/src -I$(top_builddir)/src
tests_CFLAGS = \
+ -pthread \
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
$(GPROF_CFLAGS) $(GCOV_CFLAGS) \
$(PCRE_CFLAGS)
diff --git a/tests/c-api/tests-main.c b/tests/c-api/tests-main.c
index 1e569da..fbfaefa 100644
--- a/tests/c-api/tests-main.c
+++ b/tests/c-api/tests-main.c
@@ -26,6 +26,8 @@
#include <fcntl.h>
#include <assert.h>
+#include <pthread.h>
+
#include <pcre.h>
/* Warn about deprecated libguestfs functions, but only in this file,
@@ -515,6 +517,7 @@ create_handle (void)
return g;
}
+/* Serial tests. */
static size_t
perform_tests (guestfs_h *g)
{
@@ -534,21 +537,126 @@ perform_tests (guestfs_h *g)
return nr_failed;
}
+/* Parallel tests. */
+struct thread_data {
+ pthread_t thread;
+ size_t failed;
+ guestfs_h *g;
+};
+
+static size_t test_num = 0;
+static pthread_mutex_t test_num_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void *
+start_thread (void *datavp)
+{
+ struct thread_data *data = datavp;
+ size_t n;
+ struct test *t;
+
+ /* Keep running until we've done all the tests. */
+ for (;;) {
+ pthread_mutex_lock (&test_num_mutex);
+ if (test_num >= nr_tests) {
+ pthread_mutex_unlock (&test_num_mutex);
+ break;
+ }
+ n = test_num;
+ ++test_num;
+ pthread_mutex_unlock (&test_num_mutex);
+
+ t = &tests[n];
+ //next_test (data->g, n, t->name);
+ if (t->test_fn (data->g) == -1) {
+ printf ("FAIL: %s\n", t->name);
+ data->failed++;
+ }
+ }
+
+ return NULL;
+}
+
+static size_t
+perform_parallel_tests (guestfs_h **g, size_t nr_threads)
+{
+ struct thread_data thread_data[nr_threads];
+ size_t nr_failed = 0;
+ size_t i;
+ int r;
+
+ /* Create threads. */
+ for (i = 0; i < nr_threads; ++i) {
+ thread_data[i].g = g[i];
+ thread_data[i].failed = 0;
+ r = pthread_create (&thread_data[i].thread, NULL,
+ start_thread, &thread_data[i]);
+ if (r != 0) {
+ fprintf (stderr, "pthread_create: %s\n", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Wait for the threads to exit. */
+ for (i = 0; i < nr_threads; ++i) {
+ r = pthread_join (thread_data[i].thread, NULL);
+ if (r != 0) {
+ fprintf (stderr, "pthread_join: %s\n", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ nr_failed += thread_data[i].failed;
+ }
+
+ return nr_failed;
+}
+
int
main (int argc, char *argv[])
{
+ char *parallel_str;
+ size_t parallel = 0, i;
+ guestfs_h **g;
size_t nr_failed;
- guestfs_h *g;
+
+ /* In $builddir/localenv, put:
+ *
+ * export TEST_C_API_PARALLEL=<N>
+ *
+ * to run <N> parallel threads. Or omit it, or set it to 0 to run
+ * the tests normally.
+ */
+ parallel_str = getenv ("TEST_C_API_PARALLEL");
+ if (parallel_str) {
+ if (sscanf (parallel_str, "%zu", ¶llel) != 1) {
+ fprintf (stderr, "TEST_C_API_PARALLEL is not a number (ignored)\n");
+ parallel = 0;
+ }
+ }
+
+ if (parallel == 0)
+ parallel = 1;
setbuf (stdout, NULL);
no_test_warnings ();
- g = create_handle ();
+ /* Create handle(s). */
+ g = malloc (sizeof (guestfs_h *) * parallel);
+ if (!g) {
+ perror ("malloc");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < parallel; ++i)
+ g[i] = create_handle ();
- nr_failed = perform_tests (g);
+ if (parallel == 1)
+ nr_failed = perform_tests (g[0]);
+ else
+ nr_failed = perform_parallel_tests (g, parallel);
- guestfs_close (g);
+ /* Close handles. */
+ for (i = 0; i < parallel; ++i)
+ guestfs_close (g[i]);
if (nr_failed > 0) {
printf ("***** %zu / %zu tests FAILED *****\n", nr_failed, nr_tests);
--
1.8.1.4