summaryrefslogtreecommitdiff
path: root/tests/test-random-mt.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-random-mt.c')
-rw-r--r--tests/test-random-mt.c169
1 files changed, 169 insertions, 0 deletions
diff --git a/tests/test-random-mt.c b/tests/test-random-mt.c
new file mode 100644
index 00000000..4e2f2d37
--- /dev/null
+++ b/tests/test-random-mt.c
@@ -0,0 +1,169 @@
+/* Multithread-safety test for random().
+ Copyright (C) 2023-2024 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