diff --git a/base/Base.jl b/base/Base.jl index 91094f94ec213f..bccd8e4e25e78a 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -290,6 +290,9 @@ include("process.jl") include("ttyhascolor.jl") include("secretbuffer.jl") +# RandomDevice support +include("randomdevice.jl") + # core math functions include("floatfuncs.jl") include("math.jl") diff --git a/base/libc.jl b/base/libc.jl index 8cce4ce2a259b6..38b62847eaeb4b 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -400,7 +400,7 @@ rand(::Type{Float64}) = rand(UInt32) * 2.0^-32 Interface to the C `srand(seed)` function. """ -srand(seed=floor(Int, time()) % Cuint) = ccall(:srand, Cvoid, (Cuint,), seed) +srand(seed=Base._make_uint_seed()) = ccall(:srand, Cvoid, (Cuint,), seed) struct Cpasswd username::Cstring diff --git a/base/randomdevice.jl b/base/randomdevice.jl new file mode 100644 index 00000000000000..d63ff7edc1647c --- /dev/null +++ b/base/randomdevice.jl @@ -0,0 +1,77 @@ +# 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 \ No newline at end of file diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 13e5a8c778b16b..a50f633e68a9ce 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -23,42 +23,20 @@ else # !windows 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 - mutable struct FileRef - @atomic file::Union{IOStream, Nothing} - end - - const DEV_RANDOM = FileRef(nothing) - const DEV_URANDOM = FileRef(nothing) - - function getfile(rd::RandomDevice) - ref = rd.unlimited ? DEV_URANDOM : DEV_RANDOM - fd = ref.file - if fd === nothing - fd = open(rd.unlimited ? "/dev/urandom" : "/dev/random") - old, ok = @atomicreplace ref.file nothing => fd - if !ok - close(fd) - fd = old::IOStream - end - end - return fd - end - 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.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( - (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), - A, sizeof(A))) + Base.RtlGenRandom!(A) A end else @@ -332,14 +310,7 @@ function make_seed() catch 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 + Base._ad_hoc_entropy_source() return make_seed(seed) end end @@ -428,10 +399,6 @@ for T in BitInteger_types end function __init__() - @static if !Sys.iswindows() - @atomic DEV_RANDOM.file = nothing - @atomic DEV_URANDOM.file = nothing - end seed!(GLOBAL_RNG) end