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 <br...@clisp.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

Reply via email to