[Libguestfs] [PATCH threads v2 5/5] threads: Add a test.

Richard W.M. Jones
Tue, 16 Jun 2015 16:02:24 -0000

---
 .gitignore                 |   1 +
 tests/c-api/Makefile.am    |  20 ++++++-
 tests/c-api/test-threads.c | 133 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 152 insertions(+), 2 deletions(-)
 create mode 100644 tests/c-api/test-threads.c

diff --git a/.gitignore b/.gitignore
index 6f14915..22ab687 100644
--- a/.gitignore
+++ b/.gitignore
@@ -509,6 +509,7 @@ Makefile.in
 /tests/c-api/test-pwd
 /tests/c-api/tests
 /tests/c-api/tests.c
+/tests/c-api/test-threads
 /tests/c-api/test*.tmp
 /tests/c-api/test-user-cancel
 /tests/charsets/test-charset-fidelity
diff --git a/tests/c-api/Makefile.am b/tests/c-api/Makefile.am
index 7bfffe5..55e82b2 100644
--- a/tests/c-api/Makefile.am
+++ b/tests/c-api/Makefile.am
@@ -37,7 +37,8 @@ check_PROGRAMS = \
 	test-debug-to-file \
 	test-environment \
 	test-pwd \
-	test-event-string
+	test-event-string \
+	test-threads
 if HAVE_LIBDL
 check_PROGRAMS += \
 	test-dlopen
@@ -55,7 +56,8 @@ TESTS = \
 	test-user-cancel \
 	test-debug-to-file \
 	test-environment \
-	test-event-string
+	test-event-string \
+	test-threads
 if HAVE_LIBDL
 TESTS += \
 	test-dlopen
@@ -238,6 +240,20 @@ test_event_string_LDADD = \
 	$(LTLIBINTL) \
 	$(top_builddir)/gnulib/lib/libgnu.la

+test_threads_SOURCES = test-threads.c
+test_threads_CPPFLAGS = \
+	-I$(top_srcdir)/src -I$(top_builddir)/src \
+	-I$(top_srcdir)/gnulib/lib \
+	-I$(top_builddir)/gnulib/lib
+test_threads_CFLAGS = \
+	-pthread \
+	$(WARN_CFLAGS) $(WERROR_CFLAGS)
+test_threads_LDADD = \
+	$(top_builddir)/src/libguestfs.la \
+	$(LTLIBTHREAD) \
+	$(LTLIBINTL) \
+	$(top_builddir)/gnulib/lib/libgnu.la
+
 if HAVE_LIBVIRT
 test_add_libvirt_dom_SOURCES = test-add-libvirt-dom.c
 test_add_libvirt_dom_CPPFLAGS = \
diff --git a/tests/c-api/test-threads.c b/tests/c-api/test-threads.c
new file mode 100644
index 0000000..218080f
--- /dev/null
+++ b/tests/c-api/test-threads.c
@@ -0,0 +1,133 @@
+/* libguestfs
+ * Copyright (C) 2015 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Test that we can make API calls safely from multiple threads. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <pthread.h>
+
+#include "guestfs.h"
+#include "guestfs-internal-frontend.h"
+
+static guestfs_h *g;
+
+#define RUN_TIME 60 /* seconds */
+#define NR_CONCURRENT_THREADS 4
+
+static void *start_thread (void *nullv);
+
+int
+main (int argc, char *argv[])
+{
+  time_t start_t, t;
+  pthread_t threads[NR_CONCURRENT_THREADS];
+  void *ret;
+  int i, r;
+
+  /* Because we rely on error message content below, force LC_ALL=C. */
+  setenv ("LC_ALL", "C", 1);
+
+  g = guestfs_create ();
+  if (!g) {
+    perror ("guestfs_create");
+    exit (EXIT_FAILURE);
+  }
+
+  time (&start_t);
+
+  while (time (&t), t - start_t < RUN_TIME) {
+    for (i = 0; i < NR_CONCURRENT_THREADS; ++i) {
+      r = pthread_create (&threads[i], NULL, start_thread, NULL);
+      if (r != 0) {
+        fprintf (stderr, "pthread_create: %s\n", strerror (r));
+        exit (EXIT_FAILURE);
+      }
+    }
+
+    for (i = 0; i < NR_CONCURRENT_THREADS; ++i) {
+      r = pthread_join (threads[i], &ret);
+      if (r != 0) {
+        fprintf (stderr, "pthread_join: %s\n", strerror (r));
+        exit (EXIT_FAILURE);
+      }
+      if (ret != NULL) {
+        fprintf (stderr, "thread[%d] failed\n", i);
+        exit (EXIT_FAILURE);
+      }
+    }
+  }
+
+  guestfs_close (g);
+
+  exit (EXIT_SUCCESS);
+}
+
+static void *
+start_thread (void *nullv)
+{
+  char *p;
+  const char *err;
+  int iterations;
+
+  for (iterations = 0; iterations < 1000; ++iterations) {
+    guestfs_set_hv (g, "test");
+    p = guestfs_get_hv (g);
+    if (!p || STRNEQ (p, "test")) {
+      fprintf (stderr, "invalid return from guestfs_get_hv\n");
+      pthread_exit ((void *)-1);
+    }
+    free (p);
+
+    guestfs_push_error_handler (g, NULL, NULL);
+    guestfs_set_hv (g, "test");
+    p = guestfs_get_hv (g);
+    guestfs_pop_error_handler (g);
+    if (!p || STRNEQ (p, "test")) {
+      fprintf (stderr, "invalid return from guestfs_get_hv\n");
+      pthread_exit ((void *)-1);
+    }
+    free (p);
+
+    guestfs_push_error_handler (g, NULL, NULL);
+    guestfs_set_program (g, NULL); /* deliberately cause an error */
+    guestfs_pop_error_handler (g);
+    err = guestfs_last_error (g);
+    if (!err || !STRPREFIX (err, "set_program: program: ")) {
+      fprintf (stderr, "invalid error message: %s\n", err ? err :
"NULL");
+      pthread_exit ((void *)-1);
+    }
+
+    guestfs_push_error_handler (g, NULL, NULL);
+    guestfs_set_memsize (g, 1); /* deliberately cause an error */
+    guestfs_pop_error_handler (g);
+    err = guestfs_last_error (g);
+    if (!err || strstr (err, "memsize") == NULL) {
+      fprintf (stderr, "invalid error message: %s\n", err ? err :
"NULL");
+      pthread_exit ((void *)-1);
+    }
+  }
+
+  pthread_exit (NULL);
+}
--
2.3.1