summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2012-02-15 10:40:57 +0000
committerRichard W.M. Jones <rjones@redhat.com>2012-02-15 16:59:15 +0000
commitc9ea94810e2f7a5fcce2a7136eb07ce795f49c53 (patch)
tree17ff9b862173e0f6ce96cca7564c719c2a94111e /tests
parent6a70cb337cff736831b51bbc9c4331785d756c20 (diff)
downloadlibguestfs-c9ea94810e2f7a5fcce2a7136eb07ce795f49c53.tar.gz
libguestfs-c9ea94810e2f7a5fcce2a7136eb07ce795f49c53.tar.xz
libguestfs-c9ea94810e2f7a5fcce2a7136eb07ce795f49c53.zip
tests: Test parallel launch from multiple threads.
Diffstat (limited to 'tests')
-rw-r--r--tests/regressions/Makefile.am12
-rw-r--r--tests/regressions/rhbz790721.c171
2 files changed, 182 insertions, 1 deletions
diff --git a/tests/regressions/Makefile.am b/tests/regressions/Makefile.am
index 3aed6008..a2a3673e 100644
--- a/tests/regressions/Makefile.am
+++ b/tests/regressions/Makefile.am
@@ -27,6 +27,7 @@ TESTS = \
rhbz602997.sh \
rhbz690819.sh \
rhbz789960.sh \
+ rhbz790721 \
test-noexec-stack.pl
tests_not_run = \
@@ -40,7 +41,8 @@ TESTS_ENVIRONMENT = \
$(top_builddir)/run
check_PROGRAMS = \
- rhbz501893
+ rhbz501893 \
+ rhbz790721
rhbz501893_SOURCES = rhbz501893.c
rhbz501893_CFLAGS = \
@@ -49,6 +51,14 @@ rhbz501893_CFLAGS = \
rhbz501893_LDADD = \
$(top_builddir)/src/libguestfs.la
+rhbz790721_SOURCES = rhbz790721.c
+rhbz790721_CFLAGS = \
+ -pthread \
+ -I$(top_srcdir)/src -I$(top_builddir)/src \
+ $(WARN_CFLAGS) $(WERROR_CFLAGS)
+rhbz790721_LDADD = \
+ $(top_builddir)/src/libguestfs.la
+
EXTRA_DIST = \
$(TESTS) \
$(tests_not_run) \
diff --git a/tests/regressions/rhbz790721.c b/tests/regressions/rhbz790721.c
new file mode 100644
index 00000000..b30e3db2
--- /dev/null
+++ b/tests/regressions/rhbz790721.c
@@ -0,0 +1,171 @@
+/* libguestfs
+ * Copyright (C) 2012 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.
+ */
+
+/* Regression test for RHBZ#790721.
+ *
+ * This bug involves locking issues when building the appliance in
+ * parallel from multiple threads in the same process. We use a read
+ * lock on the 'checksum' file, and it turns out this causes two
+ * problems: (1) locks don't have any effect on threads in the same
+ * process, and (2) because the PID is identical in different threads,
+ * the file we are trying to overwrite has the same name.
+ *
+ * To test this we want to create the appliance repeatedly from
+ * multiple threads, but we don't really care about launching the full
+ * qemu (a waste of time and memory for this test). Therefore replace
+ * qemu with a fake process and just look for the linking error.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pthread.h>
+
+#include "guestfs.h"
+
+/* Number of worker threads running the test. */
+#define NR_THREADS 20
+
+static pthread_barrier_t barrier;
+static void *start_thread (void *);
+
+int
+main (int argc, char *argv[])
+{
+ pthread_t thread[NR_THREADS];
+ int data[NR_THREADS];
+ int i, r, errors;
+
+ /* Ensure error messages are not translated. */
+ setenv ("LC_ALL", "C", 1);
+
+ pthread_barrier_init (&barrier, NULL, NR_THREADS);
+
+ /* Create the other threads which will set up their own libguestfs
+ * handle then wait at a barrier before launching.
+ */
+ for (i = 0; i < NR_THREADS; ++i) {
+ data[i] = i;
+ r = pthread_create (&thread[i], NULL, start_thread, &data[i]);
+ if (r != 0) {
+ fprintf (stderr, "pthread_create: %s\n", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ /* Wait for the threads to exit. */
+ errors = 0;
+
+ for (i = 0; i < NR_THREADS; ++i) {
+ int *ret;
+
+ r = pthread_join (thread[i], (void **) &ret);
+ if (r != 0) {
+ fprintf (stderr, "pthread_join: %s\n", strerror (r));
+ exit (EXIT_FAILURE);
+ }
+ if (*ret == -1)
+ errors++;
+ }
+
+ exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void *
+start_thread (void *vi)
+{
+ int thread_num = *(int *)vi;
+ guestfs_h *g;
+ int r;
+ guestfs_error_handler_cb old_error_cb;
+ void *old_error_data;
+ const char *error;
+
+ g = guestfs_create ();
+ if (g == NULL) {
+ perror ("guestfs_create");
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ if (guestfs_add_drive_opts (g, "/dev/null",
+ GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw",
+ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1,
+ -1) == -1) {
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ /* Fake out qemu. */
+ if (guestfs_set_qemu (g, "/bin/true") == -1) {
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ /* Wait for the other threads to finish starting up. */
+ r = pthread_barrier_wait (&barrier);
+ if (r != 0 && r != PTHREAD_BARRIER_SERIAL_THREAD) {
+ fprintf (stderr, "pthread_barrier_wait: %s\n", strerror (r));
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ /* Launch the handle. Because of the faked out qemu, we expect this
+ * will fail with "child process died unexpectedly". We are
+ * interested in other failures.
+ */
+ old_error_cb = guestfs_get_error_handler (g, &old_error_data);
+ guestfs_set_error_handler (g, NULL, NULL);
+ r = guestfs_launch (g);
+ error = guestfs_last_error (g);
+
+ if (r == 0) { /* This should NOT happen. */
+ fprintf (stderr, "rhbz790721: strangeness in test: expected launch to fail, but it didn't!\n");
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ if (error == NULL) { /* This also should NOT happen. */
+ fprintf (stderr, "rhbz790721: strangeness in test: no error message!\n");
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ /* If this happens, it indicates a bug/race in the appliance
+ * building code which is what this regression test is designed to
+ * spot.
+ */
+ if (strcmp (error, "child process died unexpectedly") != 0) {
+ fprintf (stderr, "rhbz790721: error: %s\n", error);
+ *(int *)vi = -1;
+ pthread_exit (vi);
+ }
+
+ guestfs_set_error_handler (g, old_error_cb, old_error_data);
+
+ /* Close the handle. */
+ guestfs_close (g);
+
+ *(int *)vi = 0;
+ pthread_exit (vi);
+}