From: Bruno Haible <bruno@clisp.org>
To: cygwin@cygwin.com
Subject: random is not multithread-safe in Cygwin
Date: Fri, 10 Nov 2023 17:19:47 +0100 [thread overview]
Message-ID: <3811044.57xzQst1vy@nimes> (raw)
[-- Attachment #1: Type: text/plain, Size: 856 bytes --]
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.
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
[-- Attachment #2: foo.c --]
[-- Type: text/x-csrc, Size: 4391 bytes --]
/* 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@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;
}
next reply other threads:[~2023-11-10 16:19 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-10 16:19 Bruno Haible [this message]
2023-11-13 16:12 ` Corinna Vinschen
2023-11-13 17:34 ` Brian Inglis
2023-11-13 18:04 ` Bruno Haible
2023-11-13 19:07 ` Corinna Vinschen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3811044.57xzQst1vy@nimes \
--to=bruno@clisp.org \
--cc=cygwin@cygwin.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).