test-user-cancel: Make this test choose smaller cancellation numbers.
authorRichard W.M. Jones <rjones@redhat.com>
Wed, 26 Oct 2011 14:55:29 +0000 (15:55 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Wed, 26 Oct 2011 16:07:02 +0000 (17:07 +0100)
Use a Gaussian distribution for random numbers so that smaller numbers
are chosen more frequently.

This also exposes a bug in this test which only happens when small
numbers are chosen:

If the test thread starts up quickly, it can get to
guestfs_user_cancel before the guestfs_upload command has happened in
the main thread.  This causes the cancel to be ignored (correctly) and
we end up in the second loop writing data until the disk runs out of
space.  Fix this by repeatedly asserting the cancel flag in the second
loop.

capitests/Makefile.am
capitests/test-user-cancel.c

index 9802a06..64fc0a1 100644 (file)
@@ -123,7 +123,7 @@ test_user_cancel_CFLAGS = \
        -I$(top_srcdir)/src -I$(top_builddir)/src \
        $(WARN_CFLAGS) $(WERROR_CFLAGS)
 test_user_cancel_LDADD = \
-       $(top_builddir)/src/libguestfs.la
+       $(top_builddir)/src/libguestfs.la -lm
 
 test_debug_to_file_SOURCES = test-debug-to-file.c
 test_debug_to_file_CFLAGS = \
index 429998e..0cc049a 100644 (file)
@@ -40,6 +40,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/time.h>
+#include <math.h>
 
 #include <pthread.h>
 
@@ -50,6 +51,7 @@ static const off_t filesize = 1024*1024*1024;
 
 static void remove_test_img (void);
 static void *start_test_thread (void *);
+static off_t random_cancel_posn (void);
 
 struct test_thread_data {
   guestfs_h *g;                /* handle */
@@ -72,7 +74,7 @@ main (int argc, char *argv[])
   int fds[2], r, op_error, op_errno, errors = 0;
   char dev_fd[64];
 
-  srandom (time (NULL));
+  srand48 (time (NULL));
 
   g = guestfs_create ();
   if (g == NULL) {
@@ -136,7 +138,7 @@ main (int argc, char *argv[])
   data.fd = fds[1];
   snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[0]);
 
-  data.cancel_posn = random () % (filesize/4);
+  data.cancel_posn = random_cancel_posn ();
 
   /* Create the test thread. */
   r = pthread_create (&test_thread, NULL, start_test_thread, &data);
@@ -197,7 +199,7 @@ main (int argc, char *argv[])
   data.fd = fds[0];
   snprintf (dev_fd, sizeof dev_fd, "/dev/fd/%d", fds[1]);
 
-  data.cancel_posn = random () % (filesize/4);
+  data.cancel_posn = random_cancel_posn ();
 
   /* Create the test thread. */
   r = pthread_create (&test_thread, NULL, start_test_thread, &data);
@@ -273,13 +275,16 @@ start_test_thread (void *datav)
       data->transfer_size += r;
     }
 
-    /* Do user cancellation. */
-    guestfs_user_cancel (data->g);
-
     /* Keep feeding data after the cancellation point for as long as
      * the main thread wants it.
      */
     while (1) {
+      /* Repeatedly assert the cancel flag.  We have to do this because
+       * the guestfs_upload command in the main thread may not have
+       * started yet.
+       */
+      guestfs_user_cancel (data->g);
+
       r = write (data->fd, buffer, sizeof buffer);
       if (r == -1) {
         perror ("test thread: write to pipe after user cancel");
@@ -325,3 +330,42 @@ start_test_thread (void *datav)
 
   return NULL;
 }
+
+static double random_gauss (double mu, double sd);
+
+/* Generate a random cancellation position, but skew it towards
+ * smaller numbers.
+ */
+static off_t
+random_cancel_posn (void)
+{
+  const double mu = 65536;
+  const double sd = 65536 * 4;
+  double r;
+
+  do {
+    r = random_gauss (mu, sd);
+  } while (r <= 0);
+
+  return (off_t) r;
+}
+
+/* Generate a random Gaussian distributed number using the Box-Muller
+ * transformation.  (http://www.taygeta.com/random/gaussian.html)
+ */
+static double
+random_gauss (double mu, double sd)
+{
+  double x1, x2, w, y1;
+
+  do {
+    x1 = 2. * drand48 () - 1.;
+    x2 = 2. * drand48 () - 1.;
+    w = x1 * x1 + x2 * x2;
+  } while (w >= 1.);
+
+  w = sqrt ((-2. * log (w)) / w);
+  y1 = x1 * w;
+  //y2 = x2 * w;
+  return mu + y1 * sd;
+}