Mail Archives: cygwin/2023/11/13/11:13:21
Hi Bruno,
On Nov 10 17:19, Bruno Haible via Cygwin wrote:
> The function 'random' is, unlike 'rand', not marked as not MT-safe in POSIX
> [1][2]. Thus it must be multithread-safe [3]:
> "Each function defined in the System Interfaces volume of POSIX.1-2017
> is thread-safe unless explicitly stated otherwise."
>
> And indeed glibc, musl libc, AIX, Android, and even NetBSD implement it in a
> multithread-safe way.
Our code is from FreeBSD, originally. I checked the latest code from
FreeBSD. It doesn't lock anything in random() and generates the same
error when running the same test app.
Why is that ok for FreeBSD?
Corinna
>
> On Cygwin 2.9.0 and 3.4.6, it is not multithread-safe.
>
> How to reproduce:
> 1. Compile the attached program.
> $ x86_64-pc-cygwin-gcc foo.c
> 2. Run it.
> $ ./a.exe
> Expected: No output.
> Actual: Output such as
> Expected value #367 not found in multithreaded results.
>
> [1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html
> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand.html
> [3] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_407
> /* 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 AT clisp DOT org>, 2023. */
>
> /* 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 1000000
>
> /* Specification. */
> #include <stdlib.h>
>
> #include <assert.h>
> #include <pthread.h>
> #include <stdio.h>
>
> #if EXPLICIT_YIELD
> # include <sched.h>
> # 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 = (long *) malloc (REPEAT_COUNT * sizeof (long));
> assert (expected != NULL);
> {
> 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. */
> pthread_t threads[THREAD_COUNT];
> long *thread_results[THREAD_COUNT];
> srandom (seed);
> {
> int i;
> for (i = 0; i < THREAD_COUNT; i++)
> {
> thread_results[i] = (long *) malloc (REPEAT_COUNT * sizeof (long));
> assert (thread_results[i] != NULL);
> }
> for (i = 0; i < THREAD_COUNT; i++)
> assert (pthread_create (&threads[i], NULL, random_invocator_thread, thread_results[i]) == 0);
> }
>
> /* Wait for the threads to terminate. */
> {
> int i;
> for (i = 0; i < THREAD_COUNT; i++)
> assert (pthread_join (threads[i], NULL) == 0);
> }
>
> /* 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;
> }
>
> --
> Problem reports: https://cygwin.com/problems.html
> FAQ: https://cygwin.com/faq/
> Documentation: https://cygwin.com/docs.html
> Unsubscribe info: https://cygwin.com/ml/#unsubscribe-simple
--
Problem reports: https://cygwin.com/problems.html
FAQ: https://cygwin.com/faq/
Documentation: https://cygwin.com/docs.html
Unsubscribe info: https://cygwin.com/ml/#unsubscribe-simple
- Raw text -