From b4bed7171730a8ea29b8ead2a16dfa7e78c2b15f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 7 Apr 2022 13:47:50 -0400 Subject: [PATCH] fix Libc.rand and seed problems (#44432) Continuation from #43606 - Replaces thread-unsafe function `rand` with `jl_rand`. - Fixes `_ad_hoc_entropy_source` fallback in Random. - Uses uv_random for more direct access to quality-randomness (usually a syscall rather than a file.) - Ensures Array{Bool} are valid when created from RandomDevice. --- base/Base.jl | 5 --- base/error.jl | 2 +- base/libc.jl | 38 ++++++++-------- base/randomdevice.jl | 77 --------------------------------- src/coverage.cpp | 4 +- src/gc-debug.c | 3 +- src/init.c | 3 +- src/jl_exported_funcs.inc | 1 - src/jl_uv.c | 9 ---- src/julia.h | 5 +-- src/julia_atomics.h | 2 +- src/julia_internal.h | 13 +++--- src/runtime_ccall.cpp | 2 +- src/signal-handling.c | 2 +- src/signals-unix.c | 2 +- src/sys.c | 34 +++++++++++++++ src/task.c | 28 ++++++------ src/threading.c | 2 +- stdlib/Random/src/RNGs.jl | 79 ++++++++++++---------------------- stdlib/Random/test/runtests.jl | 5 +-- test/error.jl | 10 ++--- 21 files changed, 120 insertions(+), 206 deletions(-) delete mode 100644 base/randomdevice.jl diff --git a/base/Base.jl b/base/Base.jl index 42e740d15916c..129dca296a845 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -300,9 +300,6 @@ include("process.jl") include("ttyhascolor.jl") include("secretbuffer.jl") -# RandomDevice support -include("randomdevice.jl") - # core math functions include("floatfuncs.jl") include("math.jl") @@ -492,8 +489,6 @@ end if is_primary_base_module function __init__() - # for the few uses of Libc.rand in Base: - Libc.srand() # Base library init reinit_stdio() Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback diff --git a/base/error.jl b/base/error.jl index 9ffcac5d7820c..4459e54def19b 100644 --- a/base/error.jl +++ b/base/error.jl @@ -261,7 +261,7 @@ function iterate(ebo::ExponentialBackOff, state= (ebo.n, min(ebo.first_delay, eb state[1] < 1 && return nothing next_n = state[1]-1 curr_delay = state[2] - next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (rand(Float64) * 2.0 * ebo.jitter))) + next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (Libc.rand(Float64) * 2.0 * ebo.jitter))) (curr_delay, (next_n, next_delay)) end length(ebo::ExponentialBackOff) = ebo.n diff --git a/base/libc.jl b/base/libc.jl index d5a54909a9700..7d88e89bf605a 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -255,7 +255,7 @@ time() = ccall(:jl_clock_now, Float64, ()) Get Julia's process ID. """ -getpid() = ccall(:jl_getpid, Int32, ()) +getpid() = ccall(:uv_os_getpid, Int32, ()) ## network functions ## @@ -376,31 +376,35 @@ free(p::Cwstring) = free(convert(Ptr{Cwchar_t}, p)) ## Random numbers ## +# Access to very high quality (kernel) randomness +function getrandom!(A::Union{Array,Base.RefValue}) + ret = ccall(:uv_random, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Cuint, Ptr{Cvoid}), + C_NULL, C_NULL, A, sizeof(A), 0, C_NULL) + Base.uv_error("getrandom", ret) + return A +end +_make_uint64_seed() = getrandom!(Base.RefValue{UInt64}())[] + # To limit dependency on rand functionality implemented in the Random module, -# Libc.rand is used in file.jl, and could be used in error.jl (but it breaks a test) +# Libc.rand is used in Base (it also is independent from Random.seed, so is +# only affected by `Libc.srand(seed)` calls) """ - rand([T::Type]) + rand([T::Type]=UInt32) -Interface to the C `rand()` function. If `T` is provided, generate a value of type `T` -by composing two calls to `rand()`. `T` can be `UInt32` or `Float64`. +Generate a random number of type `T`. `T` can be `UInt32` or `Float64`. """ -rand() = ccall(:rand, Cint, ()) -@static if Sys.iswindows() - # Windows RAND_MAX is 2^15-1 - rand(::Type{UInt32}) = ((rand() % UInt32) << 17) ⊻ ((rand() % UInt32) << 8) ⊻ (rand() % UInt32) -else - # RAND_MAX is at least 2^15-1 in theory, but we assume 2^16-1 - # on non-Windows systems (in practice, it's 2^31-1) - rand(::Type{UInt32}) = ((rand() % UInt32) << 16) ⊻ (rand() % UInt32) -end -rand(::Type{Float64}) = rand(UInt32) * 2.0^-32 +rand() = ccall(:jl_rand, UInt64, ()) % UInt32 +rand(::Type{UInt32}) = rand() +rand(::Type{Float64}) = rand() * 2.0^-32 """ srand([seed]) -Interface to the C `srand(seed)` function. +Set a value for the current global `seed`. """ -srand(seed=Base._make_uint_seed()) = ccall(:srand, Cvoid, (Cuint,), seed) +function srand(seed::Integer=_make_uint64_seed()) + ccall(:jl_srand, Cvoid, (UInt64,), seed % UInt64) +end struct Cpasswd username::Cstring diff --git a/base/randomdevice.jl b/base/randomdevice.jl deleted file mode 100644 index aaa6246e717bd..0000000000000 --- a/base/randomdevice.jl +++ /dev/null @@ -1,77 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# This file contains the minimal support of RandomDevice for Base's own usage. -# The actual RandomDevice type that makes use of this infrastructure is defined -# in the Random stdlib. - -module DevRandomState - if !Sys.iswindows() - mutable struct FileRef - @atomic file::Union{IOStream, Nothing} - end - const DEV_RANDOM = FileRef(nothing) - const DEV_URANDOM = FileRef(nothing) - end - function __init__() - if !Sys.iswindows() - @atomic DEV_RANDOM.file = nothing - @atomic DEV_URANDOM.file = nothing - end - end -end - -if Sys.iswindows() - function RtlGenRandom!(A::Union{Array, Ref}) - Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( - (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), - A, sizeof(A))) - end - - # Manually implemented to work without the Random machinery - function _rand_uint() - r = Ref{Cuint}() - RtlGenRandom!(r) - return r[] - end -else # !windows - function _get_dev_random_fd(unlimited::Bool) - ref = unlimited ? DevRandomState.DEV_URANDOM : DevRandomState.DEV_RANDOM - fd = ref.file - if fd === nothing - fd = open(unlimited ? "/dev/urandom" : "/dev/random") - old, ok = @atomicreplace ref.file nothing => fd - if !ok - close(fd) - fd = old::IOStream - end - end - return fd - end - - # Manually implemented to work without the Random machinery - function _rand_uint() - return read(_get_dev_random_fd(true), Cuint) - end -end # os-test - -function _ad_hoc_entropy() - println(stderr, - "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") - seed = reinterpret(UInt64, time()) - seed = hash(seed, getpid() % UInt) - try - seed = hash(seed, parse(UInt64, - read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], - base = 16) % UInt) - catch - end - return seed -end - -function _make_uint_seed() - try - _rand_uint() - catch - return _ad_hoc_entropy() % Cuint - end -end diff --git a/src/coverage.cpp b/src/coverage.cpp index 4ce33c105691c..46363a7e9ac01 100644 --- a/src/coverage.cpp +++ b/src/coverage.cpp @@ -201,7 +201,7 @@ extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) } else { std::string stm; - raw_string_ostream(stm) << "." << jl_getpid() << ".cov"; + raw_string_ostream(stm) << "." << uv_os_getpid() << ".cov"; write_log_data(coverageData, stm.c_str()); } } @@ -209,6 +209,6 @@ extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) extern "C" JL_DLLEXPORT void jl_write_malloc_log(void) { std::string stm; - raw_string_ostream(stm) << "." << jl_getpid() << ".mem"; + raw_string_ostream(stm) << "." << uv_os_getpid() << ".mem"; write_log_data(mallocData, stm.c_str()); } diff --git a/src/gc-debug.c b/src/gc-debug.c index 4e60aab3b0545..f199d29872697 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -467,10 +467,9 @@ static void gc_debug_alloc_init(jl_alloc_num_t *num, const char *name) return; if (*env == 'r') { env++; - srand((unsigned)uv_hrtime()); for (int i = 0;i < 3;i++) { while (num->random[i] == 0) { - num->random[i] = rand(); + num->random[i] = jl_rand(); } } } diff --git a/src/init.c b/src/init.c index 4d428b0a5a207..277a49d6df07e 100644 --- a/src/init.c +++ b/src/init.c @@ -683,11 +683,12 @@ JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel) jl_error("cannot generate code-coverage or track allocation information while generating a .o, .bc, or .s output file"); } + jl_init_rand(); jl_init_runtime_ccall(); - jl_gc_init(); jl_init_tasks(); jl_init_threading(); + jl_gc_init(); jl_ptls_t ptls = jl_init_threadtls(0); // warning: this changes `jl_current_task`, so be careful not to call that from this function jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi); diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 54658db261c2f..3a69d1b82bd82 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -201,7 +201,6 @@ XX(jl_getallocationgranularity) \ XX(jl_getnameinfo) \ XX(jl_getpagesize) \ - XX(jl_getpid) \ XX(jl_get_ARCH) \ XX(jl_get_backtrace) \ XX(jl_get_binding) \ diff --git a/src/jl_uv.c b/src/jl_uv.c index d2a78c26bbc72..dcc87d69a04fd 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -640,15 +640,6 @@ JL_DLLEXPORT void jl_exit(int exitcode) exit(exitcode); } -JL_DLLEXPORT int jl_getpid(void) JL_NOTSAFEPOINT -{ -#ifdef _OS_WINDOWS_ - return GetCurrentProcessId(); -#else - return getpid(); -#endif -} - typedef union { struct sockaddr in; struct sockaddr_in v4; diff --git a/src/julia.h b/src/julia.h index b4604ff86d00e..f156da5d77714 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1872,10 +1872,7 @@ typedef struct _jl_task_t { jl_value_t *result; jl_value_t *logstate; jl_function_t *start; - uint64_t rngState0; // really rngState[4], but more convenient to split - uint64_t rngState1; - uint64_t rngState2; - uint64_t rngState3; + uint64_t rngState[4]; _Atomic(uint8_t) _state; uint8_t sticky; // record whether this Task can be migrated to a new thread _Atomic(uint8_t) _isexception; // set if `result` is an exception to throw or that we exited with diff --git a/src/julia_atomics.h b/src/julia_atomics.h index 1f1a7a46cc9b6..cb14e535cd010 100644 --- a/src/julia_atomics.h +++ b/src/julia_atomics.h @@ -149,7 +149,7 @@ bool jl_atomic_cmpswap_explicit(std::atomic *ptr, T *expected, S val, std::me { return std::atomic_compare_exchange_strong_explicit(ptr, expected, val, order, order); } -#define jl_atomic_cmpswap_relaxed(ptr, val) jl_atomic_cmpswap_explicit(ptr, val, memory_order_relaxed) +#define jl_atomic_cmpswap_relaxed(ptr, expected, val) jl_atomic_cmpswap_explicit(ptr, expected, val, memory_order_relaxed) template T jl_atomic_exchange(std::atomic *ptr, S desired) { diff --git a/src/julia_internal.h b/src/julia_internal.h index 5e1e8b02dc6c4..1edc015a21cc7 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1113,21 +1113,19 @@ void jl_push_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT JL_ROOTING_AR //-------------------------------------------------- // congruential random number generator // for a small amount of thread-local randomness -// we could just use libc:`rand()`, but we want to ensure this is fast -STATIC_INLINE void seed_cong(uint64_t *seed) -{ - *seed = rand(); -} -STATIC_INLINE void unbias_cong(uint64_t max, uint64_t *unbias) +STATIC_INLINE void unbias_cong(uint64_t max, uint64_t *unbias) JL_NOTSAFEPOINT { *unbias = UINT64_MAX - ((UINT64_MAX % max) + 1); } -STATIC_INLINE uint64_t cong(uint64_t max, uint64_t unbias, uint64_t *seed) +STATIC_INLINE uint64_t cong(uint64_t max, uint64_t unbias, uint64_t *seed) JL_NOTSAFEPOINT { while ((*seed = 69069 * (*seed) + 362437) > unbias) ; return *seed % max; } +JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_srand(uint64_t) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_init_rand(void); JL_DLLEXPORT extern void *jl_libjulia_internal_handle; JL_DLLEXPORT extern void *jl_RTLD_DEFAULT_handle; @@ -1159,7 +1157,6 @@ JL_DLLEXPORT const char *jl_dlfind_win32(const char *name); // libuv wrappers: JL_DLLEXPORT int jl_fs_rename(const char *src_path, const char *dst_path); -int jl_getpid(void) JL_NOTSAFEPOINT; #ifdef SEGV_EXCEPTION extern JL_DLLEXPORT jl_value_t *jl_segv_exception; diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index 02523abe73479..690062b2d98fb 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -157,7 +157,7 @@ std::string jl_format_filename(StringRef output_pattern) } switch (c) { case 'p': - outfile << jl_getpid(); + outfile << uv_os_getpid(); break; case 'd': if (got_pwd) diff --git a/src/signal-handling.c b/src/signal-handling.c index c2b9f4e459167..d7876fa299a0b 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -345,7 +345,7 @@ JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec) profile_round_robin_thread_order[i] = i; } } - seed_cong(&profile_cong_rng_seed); + profile_cong_rng_seed = jl_rand(); unbias_cong(jl_n_threads, &profile_cong_rng_unbias); bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t)); if (bt_data_prof == NULL && maxsize > 0) diff --git a/src/signals-unix.c b/src/signals-unix.c index 2fda82093506b..2b399bf76190d 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -897,7 +897,7 @@ static void *signal_listener(void *arg) jl_ptls_t ptls2 = jl_all_tls_states[idx]; nrunning += !jl_atomic_load_relaxed(&ptls2->sleep_check_state); } - jl_safe_printf("\ncmd: %s %d running %d of %d\n", jl_options.julia_bin ? jl_options.julia_bin : "julia", jl_getpid(), nrunning, jl_n_threads); + jl_safe_printf("\ncmd: %s %d running %d of %d\n", jl_options.julia_bin ? jl_options.julia_bin : "julia", uv_os_getpid(), nrunning, jl_n_threads); #endif jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); diff --git a/src/sys.c b/src/sys.c index dc70302a56b15..bc21d065f55a3 100644 --- a/src/sys.c +++ b/src/sys.c @@ -715,6 +715,40 @@ JL_DLLEXPORT size_t jl_maxrss(void) #endif } +// Simple `rand()` like function, with global seed and added thread-safety +// (but slow and insecure) +static _Atomic(uint64_t) g_rngseed; +JL_DLLEXPORT uint64_t jl_rand(void) JL_NOTSAFEPOINT +{ + uint64_t max = UINT64_MAX; + uint64_t unbias = UINT64_MAX; + uint64_t rngseed0 = jl_atomic_load_relaxed(&g_rngseed); + uint64_t rngseed; + uint64_t rnd; + do { + rngseed = rngseed0; + rnd = cong(max, unbias, &rngseed); + } while (!jl_atomic_cmpswap_relaxed(&g_rngseed, &rngseed0, rngseed)); + return rnd; +} + +JL_DLLEXPORT void jl_srand(uint64_t rngseed) JL_NOTSAFEPOINT +{ + jl_atomic_store_relaxed(&g_rngseed, rngseed); +} + +void jl_init_rand(void) JL_NOTSAFEPOINT +{ + uint64_t rngseed; + if (uv_random(NULL, NULL, &rngseed, sizeof(rngseed), 0, NULL)) { + ios_puts("WARNING: Entropy pool not available to seed RNG; using ad-hoc entropy sources.\n", ios_stderr); + rngseed = uv_hrtime(); + rngseed ^= int64hash(uv_os_getpid()); + } + jl_srand(rngseed); + srand(rngseed); +} + #ifdef __cplusplus } #endif diff --git a/src/task.c b/src/task.c index dbafa0ee29e0a..52ca2ba4f2015 100644 --- a/src/task.c +++ b/src/task.c @@ -706,12 +706,12 @@ JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED) There is a pure Julia implementation in stdlib that tends to be faster when used from within Julia, due to inlining and more agressive architecture-specific optimizations. */ -JL_DLLEXPORT uint64_t jl_tasklocal_genrandom(jl_task_t *task) JL_NOTSAFEPOINT +uint64_t jl_genrandom(uint64_t rngState[4]) JL_NOTSAFEPOINT { - uint64_t s0 = task->rngState0; - uint64_t s1 = task->rngState1; - uint64_t s2 = task->rngState2; - uint64_t s3 = task->rngState3; + uint64_t s0 = rngState[0]; + uint64_t s1 = rngState[1]; + uint64_t s2 = rngState[2]; + uint64_t s3 = rngState[3]; uint64_t t = s1 << 17; uint64_t tmp = s0 + s3; @@ -723,14 +723,14 @@ JL_DLLEXPORT uint64_t jl_tasklocal_genrandom(jl_task_t *task) JL_NOTSAFEPOINT s2 ^= t; s3 = (s3 << 45) | (s3 >> 19); - task->rngState0 = s0; - task->rngState1 = s1; - task->rngState2 = s2; - task->rngState3 = s3; + rngState[0] = s0; + rngState[1] = s1; + rngState[2] = s2; + rngState[3] = s3; return res; } -void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT +static void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT { /* TODO: consider a less ad-hoc construction Ideally we could just use the output of the random stream to seed the initial @@ -748,10 +748,10 @@ void rng_split(jl_task_t *from, jl_task_t *to) JL_NOTSAFEPOINT 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 */ - to->rngState0 = 0x02011ce34bce797f * jl_tasklocal_genrandom(from); - to->rngState1 = 0x5a94851fb48a6e05 * jl_tasklocal_genrandom(from); - to->rngState2 = 0x3688cf5d48899fa7 * jl_tasklocal_genrandom(from); - to->rngState3 = 0x867b4bb4c42e5661 * jl_tasklocal_genrandom(from); + to->rngState[0] = 0x02011ce34bce797f * jl_genrandom(from->rngState); + to->rngState[1] = 0x5a94851fb48a6e05 * jl_genrandom(from->rngState); + to->rngState[2] = 0x3688cf5d48899fa7 * jl_genrandom(from->rngState); + to->rngState[3] = 0x867b4bb4c42e5661 * jl_genrandom(from->rngState); } JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion_future, size_t ssize) diff --git a/src/threading.c b/src/threading.c index ab04100083d35..0314112aca425 100644 --- a/src/threading.c +++ b/src/threading.c @@ -302,7 +302,7 @@ jl_ptls_t jl_init_threadtls(int16_t tid) { jl_ptls_t ptls = (jl_ptls_t)calloc(1, sizeof(jl_tls_states_t)); ptls->system_id = (jl_thread_t)(uintptr_t)uv_thread_self(); - seed_cong(&ptls->rngseed); + ptls->rngseed = jl_rand(); #ifdef _OS_WINDOWS_ if (tid == 0) { if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 5a33cb97a36f0..115034d3e3988 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -2,50 +2,6 @@ ## RandomDevice -if Sys.iswindows() - struct RandomDevice <: AbstractRNG - buffer::Vector{UInt128} - - RandomDevice() = new(Vector{UInt128}(undef, 1)) - end - - function rand(rd::RandomDevice, sp::SamplerBoolBitInteger) - rand!(rd, rd.buffer) - @inbounds return rd.buffer[1] % sp[] - end - - show(io::IO, ::RandomDevice) = print(io, RandomDevice, "()") - -else # !windows - struct RandomDevice <: AbstractRNG - unlimited::Bool - - RandomDevice(; unlimited::Bool=true) = new(unlimited) - end - - getfile(rd::RandomDevice) = Base._get_dev_random_fd(rd.unlimited) - - rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[]) - rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool - - show(io::IO, rd::RandomDevice) = - print(io, RandomDevice, rd.unlimited ? "()" : "(unlimited=false)") -end # os-test - -# NOTE: this can't be put within the if-else block above -for T in (Bool, BitInteger_types...) - if Sys.iswindows() - @eval function rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T}) - Base.RtlGenRandom!(A) - A - end - else - @eval rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T}) = read!(getfile(rd), A) - end -end - -# RandomDevice produces natively UInt64 -rng_native_52(::RandomDevice) = UInt64 """ RandomDevice() @@ -54,11 +10,31 @@ Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. The entropy is obtained from the operating system. """ -RandomDevice - -RandomDevice(::Nothing) = RandomDevice() +struct RandomDevice <: AbstractRNG; end +RandomDevice(seed::Nothing) = RandomDevice() seed!(rng::RandomDevice) = rng +rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = Libc.getrandom!(Ref{sp[]}())[] +rand(rd::RandomDevice, ::SamplerType{Bool}) = rand(rd, UInt8) % Bool +function rand!(rd::RandomDevice, A::Array{Bool}, ::SamplerType{Bool}) + Libc.getrandom!(A) + # we need to mask the result so that only the LSB in each byte can be non-zero + GC.@preserve A begin + p = Ptr{UInt8}(pointer(A)) + for i = 1:length(A) + unsafe_store!(p, unsafe_load(p) & 0x1) + p += 1 + end + end + return A +end +for T in BitInteger_types + @eval rand!(rd::RandomDevice, A::Array{$T}, ::SamplerType{$T}) = Libc.getrandom!(A) +end + +# RandomDevice produces natively UInt64 +rng_native_52(::RandomDevice) = UInt64 + ## MersenneTwister @@ -307,11 +283,10 @@ end function make_seed() try return rand(RandomDevice(), UInt32, 4) - catch - println(stderr, - "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") - Base._ad_hoc_entropy_source() - return make_seed(seed) + catch ex + ex isa IOError || rethrow() + @warn "Entropy pool not available to seed RNG; using ad-hoc entropy sources." + return make_seed(Libc.rand()) end end diff --git a/stdlib/Random/test/runtests.jl b/stdlib/Random/test/runtests.jl index c8be4c95cdaf2..6c79f531826bc 100644 --- a/stdlib/Random/test/runtests.jl +++ b/stdlib/Random/test/runtests.jl @@ -449,6 +449,7 @@ end @testset "rand(Bool) uniform distribution" begin for n in [rand(1:8), rand(9:16), rand(17:64)] a = zeros(Bool, n) + a8 = unsafe_wrap(Array, Ptr{UInt8}(pointer(a)), length(a); own=false) # unsafely observe the actual bit patterns in `a` as = zeros(Int, n) # we will test statistical properties for each position of a, # but also for 3 linear combinations of positions (for the array version) @@ -466,6 +467,7 @@ end end else as .+= rand!(rng, a) + @test all(x -> x === 0x00 || x === 0x01, a8) aslcs .+= [xor(getindex.(Ref(a), lcs[i])...) for i in 1:3] end end @@ -912,9 +914,6 @@ end @testset "RandomDevice" begin @test string(RandomDevice()) == "$RandomDevice()" - if !Sys.iswindows() - @test string(RandomDevice(unlimited=false)) == "$RandomDevice(unlimited=false)" - end end end diff --git a/test/error.jl b/test/error.jl index 913d303496e3e..1dae62fb91e58 100644 --- a/test/error.jl +++ b/test/error.jl @@ -6,11 +6,11 @@ @test maximum(ExponentialBackOff(n=10, max_delay=0.06)) == 0.06 ratio(x) = x[2:end]./x[1:end-1] @test all(x->x ≈ 10.0, ratio(collect(ExponentialBackOff(n=10, max_delay=Inf, factor=10, jitter=0.0)))) - Test.guardseed(12345) do - x = ratio(collect(ExponentialBackOff(n=100, max_delay=Inf, factor=1, jitter=0.1))) - xm = sum(x) / length(x) - @test abs(xm - 1.0) < 0.01 - end + Libc.srand(12345) + x = ratio(collect(ExponentialBackOff(n=100, max_delay=Inf, factor=1, jitter=0.1))) + xm = sum(x) / length(x) + @test abs(xm - 1.0) < 0.01 + Libc.srand() end @testset "retrying after errors" begin function foo_error(c, n)