1 /* Multithread-safety test for random().
2 Copyright (C) 2023-2024 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Written by Bruno Haible <bruno@clisp.org>, 2023. */
21 #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
23 /* Whether to help the scheduler through explicit yield().
24 Uncomment this to see if the operating system has a fair scheduler. */
25 #define EXPLICIT_YIELD 1
27 /* Number of simultaneous threads. */
28 #define THREAD_COUNT 4
30 /* Number of random() invocations operations performed in each thread.
31 This value is chosen so that the unit test terminates quickly.
32 To reliably determine whether a random() implementation is multithread-safe,
33 set REPEAT_COUNT to 1000000 and run the test 100 times:
34 $ for i in `seq 100`; do ./test-random-mt; done
36 #define REPEAT_COUNT 100000
47 #include "glthread/thread.h"
51 # define yield() sched_yield ()
56 /* This test runs REPEAT_COUNT invocations of random() in each thread and stores
57 the result, then compares the first REPEAT_COUNT among these
58 THREAD_COUNT * REPEAT_COUNT
59 random numbers against a precomputed sequence with the same seed. */
62 random_invocator_thread (void *arg
)
64 long *storage
= (long *) arg
;
67 for (repeat
= 0; repeat
< REPEAT_COUNT
; repeat
++)
69 storage
[repeat
] = random ();
79 unsigned int seed
= 19891109;
81 /* First, get the expected sequence of random() results. */
83 long *expected
= XNMALLOC (REPEAT_COUNT
, long);
86 for (repeat
= 0; repeat
< REPEAT_COUNT
; repeat
++)
87 expected
[repeat
] = random ();
90 /* Then, run REPEAT_COUNT invocations of random() each, in THREAD_COUNT
92 gl_thread_t threads
[THREAD_COUNT
];
93 long *thread_results
[THREAD_COUNT
];
97 for (i
= 0; i
< THREAD_COUNT
; i
++)
98 thread_results
[i
] = XNMALLOC (REPEAT_COUNT
, long);
99 for (i
= 0; i
< THREAD_COUNT
; i
++)
101 gl_thread_create (random_invocator_thread
, thread_results
[i
]);
104 /* Wait for the threads to terminate. */
107 for (i
= 0; i
< THREAD_COUNT
; i
++)
108 gl_thread_join (threads
[i
], NULL
);
111 /* Finally, determine whether the threads produced the same sequence of
115 int result_index
[THREAD_COUNT
];
118 for (i
= 0; i
< THREAD_COUNT
; i
++)
121 for (expected_index
= 0; expected_index
< REPEAT_COUNT
; expected_index
++)
123 long expected_value
= expected
[expected_index
];
125 for (i
= 0; i
< THREAD_COUNT
; i
++)
127 if (thread_results
[i
][result_index
[i
]] == expected_value
)
133 if (i
== THREAD_COUNT
)
135 if (expected_index
== 0)
137 /* This occurs on platforms like OpenBSD, where srandom() has no
138 effect and random() always return non-deterministic values.
139 Mark the test as SKIP. */
140 fprintf (stderr
, "Skipping test: random() is non-deterministic.\n");
145 fprintf (stderr
, "Expected value #%d not found in multithreaded results.\n",
158 /* No multithreading available. */
165 fputs ("Skipping test: multithreading not enabled\n", stderr
);