summaryrefslogtreecommitdiff
path: root/tests/test-random-mt.c
blob: 4e2f2d378e93bbdfc0893437f19ef1fb4310b4e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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