]> Savannah Git Hosting - gnulib.git/commitdiff
random tests: Add multithread-safety test.
authorBruno Haible <bruno@clisp.org>
Fri, 10 Nov 2023 15:48:18 +0000 (16:48 +0100)
committerBruno Haible <bruno@clisp.org>
Fri, 10 Nov 2023 15:48:18 +0000 (16:48 +0100)
* tests/test-random-mt.c: New file.
* modules/random-tests (Files): Add it.
(Depends-on): Add xalloc, thread, yield.
(Makefile.am): Also build and test test-random-mt.

ChangeLog
modules/random-tests
tests/test-random-mt.c [new file with mode: 0644]

index d38e5b56659cb6aef1c459fdb33a58db746565eb..6be74ca3c1c0fddc22deb28d3160d079101771cd 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2023-11-10  Bruno Haible  <bruno@clisp.org>
 
+       random tests: Add multithread-safety test.
+       * tests/test-random-mt.c: New file.
+       * modules/random-tests (Files): Add it.
+       (Depends-on): Add xalloc, thread, yield.
+       (Makefile.am): Also build and test test-random-mt.
+
        random: Fix multithread-safety bug in general.
        * m4/random.m4 (gl_FUNC_RANDOM): Override also macOS, FreeBSD, Solaris,
        Cygwin, Haiku.
index 27e5fb938798bfe59b0c429bfca4653c93dc4ef4..d82e73c8c8cfcaa4e6e3056324a97aa28af79676 100644 (file)
@@ -1,12 +1,17 @@
 Files:
 tests/test-random.c
+tests/test-random-mt.c
 tests/signature.h
 tests/macros.h
 
 Depends-on:
+xalloc
+thread
+yield
 
 configure.ac:
 
 Makefile.am:
-TESTS += test-random
-check_PROGRAMS += test-random
+TESTS += test-random test-random-mt
+check_PROGRAMS += test-random test-random-mt
+test_random_mt_LDADD = $(LDADD) $(LIBINTL) $(LIBMULTITHREAD) $(YIELD_LIB)
diff --git a/tests/test-random-mt.c b/tests/test-random-mt.c
new file mode 100644 (file)
index 0000000..9e95477
--- /dev/null
@@ -0,0 +1,169 @@
+/* Multithread-safety test for random().
+   Copyright (C) 2023 Free Software Foundation, 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 3 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, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
+
+#include <config.h>
+
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+
+/* Whether to help the scheduler through explicit yield().
+   Uncomment this to see if the operating system has a fair scheduler.  */
+#define EXPLICIT_YIELD 1
+
+/* Number of simultaneous threads.  */
+#define THREAD_COUNT 4
+
+/* Number of random() invocations operations performed in each thread.
+   This value is chosen so that the unit test terminates quickly.
+   To reliably determine whether a random() implementation is multithread-safe,
+   set REPEAT_COUNT to 1000000 and run the test 100 times:
+     $ for i in `seq 100`; do ./test-random-mt; done
+ */
+#define REPEAT_COUNT 100000
+
+/* Specification.  */
+#include <stdlib.h>
+
+#include <stdio.h>
+
+#if EXPLICIT_YIELD
+# include <sched.h>
+#endif
+
+#include "glthread/thread.h"
+#include "xalloc.h"
+
+#if EXPLICIT_YIELD
+# define yield() sched_yield ()
+#else
+# define yield()
+#endif
+
+/* This test runs REPEAT_COUNT invocations of random() in each thread and stores
+   the result, then compares the first REPEAT_COUNT among these
+     THREAD_COUNT * REPEAT_COUNT
+   random numbers against a precomputed sequence with the same seed.  */
+
+static void *
+random_invocator_thread (void *arg)
+{
+  long *storage = (long *) arg;
+  int repeat;
+
+  for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
+    {
+      storage[repeat] = random ();
+      yield ();
+    }
+
+  return NULL;
+}
+
+int
+main ()
+{
+  unsigned int seed = 19891109;
+
+  /* First, get the expected sequence of random() results.  */
+  srandom (seed);
+  long *expected = XNMALLOC (REPEAT_COUNT, long);
+  {
+    int repeat;
+    for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
+      expected[repeat] = random ();
+  }
+
+  /* Then, run REPEAT_COUNT invocations of random() each, in THREAD_COUNT
+     separate threads.  */
+  gl_thread_t threads[THREAD_COUNT];
+  long *thread_results[THREAD_COUNT];
+  srandom (seed);
+  {
+    int i;
+    for (i = 0; i < THREAD_COUNT; i++)
+      thread_results[i] = XNMALLOC (REPEAT_COUNT, long);
+    for (i = 0; i < THREAD_COUNT; i++)
+      threads[i] =
+        gl_thread_create (random_invocator_thread, thread_results[i]);
+  }
+
+  /* Wait for the threads to terminate.  */
+  {
+    int i;
+    for (i = 0; i < THREAD_COUNT; i++)
+      gl_thread_join (threads[i], NULL);
+  }
+
+  /* Finally, determine whether the threads produced the same sequence of
+     random() results.  */
+  {
+    int expected_index;
+    int result_index[THREAD_COUNT];
+    int i;
+
+    for (i = 0; i < THREAD_COUNT; i++)
+      result_index[i] = 0;
+
+    for (expected_index = 0; expected_index < REPEAT_COUNT; expected_index++)
+      {
+        long expected_value = expected[expected_index];
+
+        for (i = 0; i < THREAD_COUNT; i++)
+          {
+            if (thread_results[i][result_index[i]] == expected_value)
+              {
+                result_index[i]++;
+                break;
+              }
+          }
+        if (i == THREAD_COUNT)
+          {
+            if (expected_index == 0)
+              {
+                /* This occurs on platforms like OpenBSD, where srandom() has no
+                   effect and random() always return non-deterministic values.
+                   Mark the test as SKIP.  */
+                fprintf (stderr, "Skipping test: random() is non-deterministic.\n");
+                return 77;
+              }
+            else
+              {
+                fprintf (stderr, "Expected value #%d not found in multithreaded results.\n",
+                         expected_index);
+                return 1;
+              }
+          }
+      }
+  }
+
+  return 0;
+}
+
+#else
+
+/* No multithreading available.  */
+
+#include <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: multithreading not enabled\n", stderr);
+  return 77;
+}
+
+#endif