(root)/
gettext-0.22.4/
gettext-tools/
gnulib-tests/
test-random-mt.c
       1  /* Multithread-safety test for random().
       2     Copyright (C) 2023 Free Software Foundation, Inc.
       3  
       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.
       8  
       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.
      13  
      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/>.  */
      16  
      17  /* Written by Bruno Haible <bruno@clisp.org>, 2023.  */
      18  
      19  #include <config.h>
      20  
      21  #if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
      22  
      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
      26  
      27  /* Number of simultaneous threads.  */
      28  #define THREAD_COUNT 4
      29  
      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
      35   */
      36  #define REPEAT_COUNT 100000
      37  
      38  /* Specification.  */
      39  #include <stdlib.h>
      40  
      41  #include <stdio.h>
      42  
      43  #if EXPLICIT_YIELD
      44  # include <sched.h>
      45  #endif
      46  
      47  #include "glthread/thread.h"
      48  #include "xalloc.h"
      49  
      50  #if EXPLICIT_YIELD
      51  # define yield() sched_yield ()
      52  #else
      53  # define yield()
      54  #endif
      55  
      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.  */
      60  
      61  static void *
      62  random_invocator_thread (void *arg)
      63  {
      64    long *storage = (long *) arg;
      65    int repeat;
      66  
      67    for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
      68      {
      69        storage[repeat] = random ();
      70        yield ();
      71      }
      72  
      73    return NULL;
      74  }
      75  
      76  int
      77  main ()
      78  {
      79    unsigned int seed = 19891109;
      80  
      81    /* First, get the expected sequence of random() results.  */
      82    srandom (seed);
      83    long *expected = XNMALLOC (REPEAT_COUNT, long);
      84    {
      85      int repeat;
      86      for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
      87        expected[repeat] = random ();
      88    }
      89  
      90    /* Then, run REPEAT_COUNT invocations of random() each, in THREAD_COUNT
      91       separate threads.  */
      92    gl_thread_t threads[THREAD_COUNT];
      93    long *thread_results[THREAD_COUNT];
      94    srandom (seed);
      95    {
      96      int i;
      97      for (i = 0; i < THREAD_COUNT; i++)
      98        thread_results[i] = XNMALLOC (REPEAT_COUNT, long);
      99      for (i = 0; i < THREAD_COUNT; i++)
     100        threads[i] =
     101          gl_thread_create (random_invocator_thread, thread_results[i]);
     102    }
     103  
     104    /* Wait for the threads to terminate.  */
     105    {
     106      int i;
     107      for (i = 0; i < THREAD_COUNT; i++)
     108        gl_thread_join (threads[i], NULL);
     109    }
     110  
     111    /* Finally, determine whether the threads produced the same sequence of
     112       random() results.  */
     113    {
     114      int expected_index;
     115      int result_index[THREAD_COUNT];
     116      int i;
     117  
     118      for (i = 0; i < THREAD_COUNT; i++)
     119        result_index[i] = 0;
     120  
     121      for (expected_index = 0; expected_index < REPEAT_COUNT; expected_index++)
     122        {
     123          long expected_value = expected[expected_index];
     124  
     125          for (i = 0; i < THREAD_COUNT; i++)
     126            {
     127              if (thread_results[i][result_index[i]] == expected_value)
     128                {
     129                  result_index[i]++;
     130                  break;
     131                }
     132            }
     133          if (i == THREAD_COUNT)
     134            {
     135              if (expected_index == 0)
     136                {
     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");
     141                  return 77;
     142                }
     143              else
     144                {
     145                  fprintf (stderr, "Expected value #%d not found in multithreaded results.\n",
     146                           expected_index);
     147                  return 1;
     148                }
     149            }
     150        }
     151    }
     152  
     153    return 0;
     154  }
     155  
     156  #else
     157  
     158  /* No multithreading available.  */
     159  
     160  #include <stdio.h>
     161  
     162  int
     163  main ()
     164  {
     165    fputs ("Skipping test: multithreading not enabled\n", stderr);
     166    return 77;
     167  }
     168  
     169  #endif