diff options
Diffstat (limited to 'tests/test-random-mt.c')
-rw-r--r-- | tests/test-random-mt.c | 169 |
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 |