diff --git a/src/core/ddsi/src/q_xmsg.c b/src/core/ddsi/src/q_xmsg.c index ec1495d..d1c83cb 100644 --- a/src/core/ddsi/src/q_xmsg.c +++ b/src/core/ddsi/src/q_xmsg.c @@ -1261,7 +1261,7 @@ static ssize_t nn_xpack_send1 (const nn_locator_t *loc, void * varg) { /* We drop APPROXIMATELY a fraction of xmit_lossiness * 10**(-3) of all packets to be sent */ - if ((ddsrt_random () % 1000) < config.xmit_lossiness) + if ((ddsrt_random () % 1000) < (uint32_t) config.xmit_lossiness) { DDS_TRACE("(dropped)"); xp->call_flags = 0; diff --git a/src/core/xtests/CMakeLists.txt b/src/core/xtests/CMakeLists.txt index 9a67a6c..0b1d362 100644 --- a/src/core/xtests/CMakeLists.txt +++ b/src/core/xtests/CMakeLists.txt @@ -11,7 +11,7 @@ # idlc_generate(RhcTypes RhcTypes.idl) -add_executable(rhc_torture rhc_torture.c mt19937ar.c mt19937ar.h) +add_executable(rhc_torture rhc_torture.c) target_include_directories( rhc_torture PRIVATE diff --git a/src/core/xtests/mt19937ar.c b/src/core/xtests/mt19937ar.c deleted file mode 100644 index 31e347e..0000000 --- a/src/core/xtests/mt19937ar.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright(c) 2019 ADLINK Technology Limited and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License - * v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ - -/* - A C-program for MT19937, with initialization improved 2002/1/26. - Coded by Takuji Nishimura and Makoto Matsumoto. - - Before using, initialize the state by using init_genrand(seed) - or init_by_array(init_key, key_length). - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - Any feedback is very welcome. - http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html - email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) -*/ - -#include "mt19937ar.h" - -/* Period parameters */ -#define N 624 -#define M 397 -#define MATRIX_A 0x9908b0dfU /* constant vector a */ -#define UPPER_MASK 0x80000000U /* most significant w-r bits */ -#define LOWER_MASK 0x7fffffffU /* least significant r bits */ - -static uint32_t mt[N]; /* the array for the state vector */ -static uint32_t mti=N+1; /* mti==N+1 means mt[N] is not initialized */ - -/* initializes mt[N] with a seed */ -void init_genrand(uint32_t s) -{ - mt[0]= s & 0xffffffffU; - for (mti=1; mti> 30)) + mti); - /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ - /* In the previous versions, MSBs of the seed affect */ - /* only MSBs of the array mt[]. */ - /* 2002/01/09 modified by Makoto Matsumoto */ - mt[mti] &= 0xffffffffU; - /* for >32 bit machines */ - } -} - -/* initialize by an array with array-length */ -/* init_key is the array for initializing keys */ -/* key_length is its length */ -/* slight change for C++, 2004/2/26 */ -void init_by_array(uint32_t init_key[], size_t key_length) -{ - uint32_t i, j, k; - init_genrand(19650218U); - i=1; j=0; - k = (N>key_length ? N : (uint32_t)key_length); - for (; k; k--) { - mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525U)) - + init_key[j] + j; /* non linear */ - mt[i] &= 0xffffffffU; /* for WORDSIZE > 32 machines */ - i++; j++; - if (i>=N) { mt[0] = mt[N-1]; i=1; } - if (j>=(uint32_t)key_length) j=0; - } - for (k=N-1; k; k--) { - mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941U)) - - i; /* non linear */ - mt[i] &= 0xffffffffU; /* for WORDSIZE > 32 machines */ - i++; - if (i>=N) { mt[0] = mt[N-1]; i=1; } - } - - mt[0] = 0x80000000U; /* MSB is 1; assuring non-zero initial array */ -} - -/* generates a random number on [0,0xffffffff]-interval */ -uint32_t genrand_int32(void) -{ - uint32_t y; - static uint32_t mag01[2]={0x0U, MATRIX_A}; - /* mag01[x] = x * MATRIX_A for x=0,1 */ - - if (mti >= N) { /* generate N words at one time */ - int kk; - - if (mti == N+1) /* if init_genrand() has not been called, */ - init_genrand(5489U); /* a default initial seed is used */ - - for (kk=0;kk> 1) ^ mag01[y & 0x1U]; - } - for (;kk> 1) ^ mag01[y & 0x1U]; - } - y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); - mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1U]; - - mti = 0; - } - - y = mt[mti++]; - - /* Tempering */ - y ^= (y >> 11); - y ^= (y << 7) & 0x9d2c5680U; - y ^= (y << 15) & 0xefc60000U; - y ^= (y >> 18); - - return (uint32_t) y; -} - -/* generates a random number on [0,0x7fffffff]-interval */ -int32_t genrand_int31(void) -{ - return (int32_t)(genrand_int32()>>1); -} - -/* generates a random number on [0,1]-real-interval */ -double genrand_real1(void) -{ - return genrand_int32()*(1.0/4294967295.0); - /* divided by 2^32-1 */ -} - -/* generates a random number on [0,1)-real-interval */ -double genrand_real2(void) -{ - return genrand_int32()*(1.0/4294967296.0); - /* divided by 2^32 */ -} - -/* generates a random number on (0,1)-real-interval */ -double genrand_real3(void) -{ - return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0); - /* divided by 2^32 */ -} - -/* generates a random number on [0,1) with 53-bit resolution*/ -double genrand_res53(void) -{ - uint32_t a=genrand_int32()>>5, b=genrand_int32()>>6; - return(a*67108864.0+b)*(1.0/9007199254740992.0); -} -/* These real versions are due to Isaku Wada, 2002/01/09 added */ diff --git a/src/core/xtests/mt19937ar.h b/src/core/xtests/mt19937ar.h deleted file mode 100644 index aaa08c8..0000000 --- a/src/core/xtests/mt19937ar.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright(c) 2019 ADLINK Technology Limited and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License - * v. 1.0 which is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -#ifndef MT19937AR_H -#define MT19937AR_H - -#include -#include - -/* initializes mt[N] with a seed */ -void init_genrand(uint32_t s); - -/* initialize by an array with array-length */ -/* init_key is the array for initializing keys */ -/* key_length is its length */ -/* slight change for C++, 2004/2/26 */ -void init_by_array(uint32_t init_key[], size_t key_length); - -/* generates a random number on [0,0xffffffff]-interval */ -uint32_t genrand_int32(void); - -/* generates a random number on [0,0x7fffffff]-interval */ -int32_t genrand_int31(void); - -/* generates a random number on [0,1]-real-interval */ -double genrand_real1(void); - -/* generates a random number on [0,1)-real-interval */ -double genrand_real2(void); - -/* generates a random number on (0,1)-real-interval */ -double genrand_real3(void); - -/* generates a random number on [0,1) with 53-bit resolution*/ -double genrand_res53(void); -/* These real versions are due to Isaku Wada, 2002/01/09 added */ - -#endif diff --git a/src/core/xtests/rhc_torture.c b/src/core/xtests/rhc_torture.c index 827d04d..bc7c4fb 100644 --- a/src/core/xtests/rhc_torture.c +++ b/src/core/xtests/rhc_torture.c @@ -18,6 +18,7 @@ #include "dds/ddsrt/heap.h" #include "dds/ddsrt/process.h" #include "dds/ddsrt/sync.h" +#include "dds/ddsrt/random.h" #include "dds/dds.h" #include "dds/ddsi/ddsi_tkmap.h" #include "dds__entity.h" @@ -32,7 +33,6 @@ #include "dds__rhc.h" #include "dds/ddsi/ddsi_iid.h" -#include "mt19937ar.h" #include "RhcTypes.h" #ifndef _MSC_VER @@ -41,6 +41,8 @@ #define STATIC_ARRAY_DIM #endif +static ddsrt_prng_t prng; + static struct ddsi_sertopic *mdtopic; static struct thread_state1 *mainthread; static dds_time_t tref_dds; @@ -669,14 +671,14 @@ static void test_conditions (dds_entity_t pp, dds_entity_t tp, const int count, int lastprint_pct = 0; for (int i = 0; i < count; i++) { - const int32_t keyval = (int32_t) (genrand_int32 () % N_KEYVALS); - const uint32_t which = genrand_int32 () % 3; + const int32_t keyval = (int32_t) (ddsrt_prng_random (&prng) % N_KEYVALS); + const uint32_t which = ddsrt_prng_random (&prng) % 3; uint32_t oper_base; uint32_t oper; /* generate uniform number in range 0 .. N, then map to operation following the frequency table */ do { - oper_base = genrand_int32 (); + oper_base = ddsrt_prng_random (&prng); } while (oper_base >= opthres[sizeof (opfreqs) / sizeof (opfreqs[0]) - 1]); for (oper = 0; oper < sizeof (opfreqs) / sizeof (opfreqs[0]); oper++) { @@ -745,19 +747,19 @@ static void test_conditions (dds_entity_t pp, dds_entity_t tp, const int count, tkall (rhc[k], NULL, print && k == 0, states_seen); break; case 8: { - uint32_t cond = genrand_int32 () % (uint32_t) nconds; + uint32_t cond = ddsrt_prng_random (&prng) % (uint32_t) nconds; for (size_t k = 0; k < nrd; k++) rdcond (rhc[k], rhcconds[cond], NULL, 0, print && k == 0, states_seen); break; } case 9: { - uint32_t cond = genrand_int32 () % (uint32_t) nconds; + uint32_t cond = ddsrt_prng_random (&prng) % (uint32_t) nconds; for (size_t k = 0; k < nrd; k++) tkcond (rhc[k], rhcconds[cond], NULL, 0, print && k == 0, states_seen); break; } case 10: { - uint32_t cond = genrand_int32 () % (uint32_t) nconds; + uint32_t cond = ddsrt_prng_random (&prng) % (uint32_t) nconds; for (size_t k = 0; k < nrd; k++) tkcond (rhc[k], rhcconds[cond], NULL, 1, print && k == 0, states_seen); break; @@ -826,7 +828,7 @@ int main (int argc, char **argv) print = (atoi (argv[4]) != 0); printf ("prng seed %u first %d count %d print %d\n", seed, first, count, print); - init_genrand (seed); + ddsrt_prng_init_simple (&prng, seed); memset (rres_mseq, 0, sizeof (rres_mseq)); for (size_t i = 0; i < sizeof (rres_iseq) / sizeof(rres_iseq[0]); i++) diff --git a/src/ddsrt/CMakeLists.txt b/src/ddsrt/CMakeLists.txt index 5179742..7830104 100644 --- a/src/ddsrt/CMakeLists.txt +++ b/src/ddsrt/CMakeLists.txt @@ -165,7 +165,7 @@ find_package(Threads REQUIRED) target_link_libraries(ddsrt INTERFACE Threads::Threads) if(WIN32) - target_link_libraries(ddsrt INTERFACE wsock32 ws2_32 iphlpapi) + target_link_libraries(ddsrt INTERFACE wsock32 ws2_32 iphlpapi bcrypt) elseif(UNIX) check_library_exists(c clock_gettime "" HAVE_CLOCK_GETTIME) if(NOT HAVE_CLOCK_GETTIME) diff --git a/src/ddsrt/include/dds/ddsrt/random.h b/src/ddsrt/include/dds/ddsrt/random.h index 4e44c82..0ef600f 100644 --- a/src/ddsrt/include/dds/ddsrt/random.h +++ b/src/ddsrt/include/dds/ddsrt/random.h @@ -12,13 +12,34 @@ #ifndef DDSRT_RANDOM_H #define DDSRT_RANDOM_H +#include +#include #include "dds/export.h" #if defined (__cplusplus) extern "C" { #endif -DDS_EXPORT long ddsrt_random(void); +#define DDSRT_MT19937_N 624 + +typedef struct ddsrt_prng_seed { + uint32_t key[8]; +} ddsrt_prng_seed_t; + +typedef struct ddsrt_prng { + uint32_t mt[DDSRT_MT19937_N]; + uint32_t mti; +} ddsrt_prng_t; + +DDS_EXPORT void ddsrt_random_init (void); +DDS_EXPORT void ddsrt_random_fini (void); + +DDS_EXPORT void ddsrt_prng_init_simple (ddsrt_prng_t *prng, uint32_t seed); +DDS_EXPORT bool ddsrt_prng_makeseed (struct ddsrt_prng_seed *seed); +DDS_EXPORT void ddsrt_prng_init (ddsrt_prng_t *prng, const struct ddsrt_prng_seed *seed); +DDS_EXPORT uint32_t ddsrt_prng_random (ddsrt_prng_t *prng); + +DDS_EXPORT uint32_t ddsrt_random (void); #if defined (__cplusplus) } diff --git a/src/ddsrt/src/cdtors.c b/src/ddsrt/src/cdtors.c index a3088fb..3888c87 100644 --- a/src/ddsrt/src/cdtors.c +++ b/src/ddsrt/src/cdtors.c @@ -13,6 +13,7 @@ #include "dds/ddsrt/cdtors.h" #include "dds/ddsrt/sync.h" #include "dds/ddsrt/time.h" +#include "dds/ddsrt/random.h" #if _WIN32 /* Sockets API initialization is only necessary on Microsoft Windows. The @@ -41,6 +42,7 @@ retry: ddsrt_winsock_init(); ddsrt_time_init(); #endif + ddsrt_random_init(); ddsrt_atomic_or32(&init_status, INIT_STATUS_OK); } else { while (v > 1 && !(v & INIT_STATUS_OK)) { @@ -65,6 +67,7 @@ void ddsrt_fini (void) if (nv == 1) { ddsrt_mutex_destroy(&init_mutex); + ddsrt_random_fini(); #if _WIN32 ddsrt_winsock_fini(); ddsrt_time_fini(); diff --git a/src/ddsrt/src/random.c b/src/ddsrt/src/random.c index 582cb48..7f2943a 100644 --- a/src/ddsrt/src/random.c +++ b/src/ddsrt/src/random.c @@ -9,36 +9,197 @@ * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + #include #include +#include #include "dds/ddsrt/random.h" +#include "dds/ddsrt/sync.h" +#include "dds/ddsrt/time.h" +#include "dds/ddsrt/process.h" -#if _WIN32 -#define _CRT_RAND_S -#include +#define N DDSRT_MT19937_N +#define M 397 +#define MATRIX_A 0x9908b0dfU /* constant vector a */ +#define UPPER_MASK 0x80000000U /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffU /* least significant r bits */ -long random (void) +static ddsrt_prng_t default_prng; +static ddsrt_mutex_t default_prng_lock; + +/* initializes mt[N] with a seed */ +static void init_genrand (ddsrt_prng_t *prng, uint32_t s) { - /* rand() is a really terribly bad PRNG */ - /* FIXME: Indeed (especially if not seeded), use rand_s instead. */ - union { long x; unsigned char c[4]; } t; - int i; - for (i = 0; i < 4; i++) - t.c[i] = (unsigned char) ((rand () >> 4) & 0xff); -#if RAND_MAX == INT32_MAX || RAND_MAX == 0x7fff - t.x &= RAND_MAX; -#elif RAND_MAX <= 0x7ffffffe - t.x %= (RAND_MAX+1); -#else -#error "RAND_MAX out of range" -#endif - return t.x; + prng->mt[0] = s; + for (prng->mti = 1; prng->mti < N; prng->mti++) + { + prng->mt[prng->mti] = (1812433253U * (prng->mt[prng->mti-1] ^ (prng->mt[prng->mti-1] >> 30)) + prng->mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + } } -#endif -long ddsrt_random(void) +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +static void init_by_array (ddsrt_prng_t *prng, const uint32_t init_key[], size_t key_length) { - /* FIXME: Not MT-safe, should use random_r (or a real PRNG) instead. */ - return random(); + uint32_t i, j, k; + init_genrand (prng, 19650218U); + i = 1; j = 0; + k = (N > key_length ? N : (uint32_t) key_length); + for (; k; k--) + { + prng->mt[i] = (prng->mt[i] ^ ((prng->mt[i-1] ^ (prng->mt[i-1] >> 30)) * 1664525U)) + init_key[j] + j; /* non linear */ + i++; j++; + if (i >= N) + { + prng->mt[0] = prng->mt[N-1]; + i=1; + } + if (j >= key_length) + { + j = 0; + } + } + for (k = N-1; k; k--) + { + prng->mt[i] = (prng->mt[i] ^ ((prng->mt[i-1] ^ (prng->mt[i-1] >> 30)) * 1566083941U)) - i; /* non linear */ + i++; + if (i >= N) + { + prng->mt[0] = prng->mt[N-1]; + i = 1; + } + } + prng->mt[0] = 0x80000000U; /* MSB is 1; assuring non-zero initial array */ +} + +void ddsrt_prng_init_simple (ddsrt_prng_t *prng, uint32_t seed) +{ + init_genrand (prng, seed); +} + +void ddsrt_prng_init (ddsrt_prng_t *prng, const struct ddsrt_prng_seed *seed) +{ + init_by_array (prng, seed->key, sizeof (seed->key) / sizeof (seed->key[0])); +} + +/* generates a random number on [0,0xffffffff]-interval */ +uint32_t ddsrt_prng_random (ddsrt_prng_t *prng) +{ + /* mag01[x] = x * MATRIX_A for x=0,1 */ + static const uint32_t mag01[2] = { 0x0U, MATRIX_A }; + uint32_t y; + + if (prng->mti >= N) + { + /* generate N words at one time */ + int kk; + + for (kk=0; kk < N-M; kk++) + { + y = (prng->mt[kk] & UPPER_MASK) | (prng->mt[kk+1] & LOWER_MASK); + prng->mt[kk] = prng->mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1U]; + } + for (; kk < N-1; kk++) + { + y = (prng->mt[kk] & UPPER_MASK) | (prng->mt[kk+1] & LOWER_MASK); + prng->mt[kk] = prng->mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1U]; + } + y = (prng->mt[N-1] & UPPER_MASK) | (prng->mt[0] & LOWER_MASK); + prng->mt[N-1] = prng->mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1U]; + + prng->mti = 0; + } + + y = prng->mt[prng->mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680U; + y ^= (y << 15) & 0xefc60000U; + y ^= (y >> 18); + + return y; +} + +uint32_t ddsrt_random (void) +{ + uint32_t x; + ddsrt_mutex_lock (&default_prng_lock); + x = ddsrt_prng_random (&default_prng); + ddsrt_mutex_unlock (&default_prng_lock); + return x; +} + +void ddsrt_random_init (void) +{ + ddsrt_prng_seed_t seed; + if (!ddsrt_prng_makeseed (&seed)) + { + /* Poor man's initialisation */ + struct lengthof_seed_large_enough { + char ok[sizeof (seed.key) / sizeof (seed.key[0]) >= 3 ? 1 : -1]; + }; + memset (&seed, 0, sizeof (seed)); + dds_time_t now = dds_time (); + seed.key[0] = (uint32_t) ddsrt_getpid (); + seed.key[1] = (uint32_t) ((uint64_t) now >> 32); + seed.key[2] = (uint32_t) now; + } + ddsrt_prng_init (&default_prng, &seed); + ddsrt_mutex_init (&default_prng_lock); +} + +void ddsrt_random_fini (void) +{ + ddsrt_mutex_destroy (&default_prng_lock); } diff --git a/src/ddsrt/src/random/posix/random.c b/src/ddsrt/src/random/posix/random.c new file mode 100644 index 0000000..067f42b --- /dev/null +++ b/src/ddsrt/src/random/posix/random.c @@ -0,0 +1,17 @@ +#include +#include +#include "dds/ddsrt/random.h" + +bool ddsrt_prng_makeseed (struct ddsrt_prng_seed *seed) +{ + FILE *rndfile; + memset (seed->key, 0, sizeof (seed->key)); + if ((rndfile = fopen ("/dev/urandom", "rb")) == NULL) + return false; + else + { + size_t n = fread (seed->key, sizeof (seed->key), 1, rndfile); + fclose (rndfile); + return (n == 1); + } +} diff --git a/src/ddsrt/src/random/windows/random.c b/src/ddsrt/src/random/windows/random.c new file mode 100644 index 0000000..6c912b3 --- /dev/null +++ b/src/ddsrt/src/random/windows/random.c @@ -0,0 +1,14 @@ +#include +#define WIN32_NO_STATUS +#include +#include "bcrypt.h" +#include +#include "dds/ddsrt/random.h" + +bool ddsrt_prng_makeseed (struct ddsrt_prng_seed *seed) +{ + NTSTATUS res; + memset (seed->key, 0, sizeof (seed->key)); + res = BCryptGenRandom (NULL, (PUCHAR) seed->key, (ULONG) sizeof (seed->key), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + return (res >= 0); +} diff --git a/src/ddsrt/tests/CMakeLists.txt b/src/ddsrt/tests/CMakeLists.txt index 2b3483d..552cfd2 100644 --- a/src/ddsrt/tests/CMakeLists.txt +++ b/src/ddsrt/tests/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources "thread_cleanup.c" "string.c" "log.c" + "random.c" "strlcpy.c" "socket.c" "select.c") diff --git a/src/ddsrt/tests/random.c b/src/ddsrt/tests/random.c new file mode 100644 index 0000000..f1f2dca --- /dev/null +++ b/src/ddsrt/tests/random.c @@ -0,0 +1,92 @@ +/* + * Copyright(c) 2006 to 2018 ADLINK Technology Limited and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include +#include +#include + +#include "CUnit/Test.h" +#include "dds/ddsrt/random.h" + +#define N_PRNG 4 +#define N_DATA 10 + +CU_Test(ddsrt_random, mt19937) +{ + static const uint32_t refdata[N_DATA] = { + 3499211612u, 581869302u, 3890346734u, 3586334585u, 545404204u, + 4161255391u, 3922919429u, 949333985u, 2715962298u, 1323567403u + }; + ddsrt_prng_t prng; + ddsrt_prng_init_simple (&prng, 5489U); + for (size_t i = 0; i < N_DATA; i++) + { + uint32_t x = ddsrt_prng_random (&prng); + CU_ASSERT_EQUAL_FATAL(x, refdata[i]); + } +} + +CU_Test(ddsrt_random, makeseed) +{ + ddsrt_prng_seed_t seeds[N_PRNG]; + + /* Until proven otherwise, assume all platforms have a good way of getting multiple seeds */ + memset (seeds, 0, sizeof (seeds)); + for (size_t i = 0; i < N_PRNG; i++) + { + bool ok = ddsrt_prng_makeseed (&seeds[i]); + CU_ASSERT_FATAL(ok); + } + + /* Any pair the same is possible, but the likelihood should be so small that it is worth accepting + an intermittently failing test */ + for (size_t i = 0; i < N_PRNG; i++) + { + for (size_t j = i + 1; j < N_PRNG; j++) + CU_ASSERT_FATAL (memcmp (&seeds[i], &seeds[j], sizeof (seeds[i])) != 0); + } + + /* A short random sequence generated from each of the different seeds should be unique -- again, + there is no guarantee but only an overwhelming likelihood */ + ddsrt_prng_t prngs[N_PRNG]; + uint32_t data[N_PRNG][N_DATA]; + memset (data, 0, sizeof (data)); + for (size_t i = 0; i < N_PRNG; i++) + { + ddsrt_prng_init (&prngs[i], &seeds[i]); + for (size_t j = 0; j < N_DATA; j++) + data[i][j] = ddsrt_prng_random (&prngs[i]); + } + for (size_t i = 0; i < N_PRNG; i++) + { + for (size_t j = i + 1; j < N_PRNG; j++) + CU_ASSERT_FATAL (memcmp (&data[i], &data[j], sizeof (data[i])) != 0); + } +} + +CU_Test(ddsrt_random, default_random) +{ +#define N_BINS 128 +#define N_PER_BIN 100 + uint32_t bins[N_BINS]; + memset (bins, 0, sizeof (bins)); + for (size_t i = 0; i < N_PER_BIN * N_BINS; i++) + { + uint32_t x = ddsrt_random (); + bins[x % N_BINS]++; + } + double chisq = 0.0; + for (size_t i = 0; i < N_BINS; i++) + chisq += ((bins[i] - N_PER_BIN) * (bins[i] - N_PER_BIN)) / (double) N_PER_BIN; + /* Solve[CDF[ChiSquareDistribution[127], x] == 999/1000] */ + CU_ASSERT (chisq < 181.993); +}