From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2155) id 7B3BC3857C43; Mon, 13 Nov 2023 16:12:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7B3BC3857C43 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cygwin.com; s=default; t=1699891961; bh=8+JZ/jm4YBOFykRUh4bAaFK0h4YYazzkaEOPCpz7tH8=; h=Date:From:To:Cc:Subject:Reply-To:References:In-Reply-To:From; b=q4Op2ChH7Vu6mQIbXCQIWP8GjtXagCtj9RqOF0JeEzygXDdRV7AlDi1yiNrUrc9jx JDJV6lOexOMjGX06vtJlwRnAKbnFMCgMOGk4AAmiroEX4DuQf0EhGa39Q6FCPs2S4Z ihW7egqPF48Bg0kxEjZ/zXG8/9pNZlwVppJbHjfo= Received: by calimero.vinschen.de (Postfix, from userid 500) id 5323EA80A3D; Mon, 13 Nov 2023 17:12:39 +0100 (CET) Date: Mon, 13 Nov 2023 17:12:39 +0100 From: Corinna Vinschen To: Bruno Haible Cc: cygwin@cygwin.com Subject: Re: random is not multithread-safe in Cygwin Message-ID: Reply-To: cygwin@cygwin.com Mail-Followup-To: Bruno Haible , cygwin@cygwin.com References: <3811044.57xzQst1vy@nimes> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline In-Reply-To: <3811044.57xzQst1vy@nimes> List-Id: 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 . */ > > /* Written by Bruno Haible , 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 > > #include > #include > #include > > #if EXPLICIT_YIELD > # include > # 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