From 204723ef8db147b27d2767009e65caa1b76db64a Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Thu, 14 May 2020 00:47:19 -0700 Subject: [PATCH] Move RandomDevice to Base.CoreRandom --- base/Base.jl | 1 + base/random.jl | 66 +++++++++++++++++++++++++++++++++++++++ stdlib/Random/src/RNGs.jl | 65 ++++++++------------------------------ 3 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 base/random.jl diff --git a/base/Base.jl b/base/Base.jl index c5dd34271493d..f1f2be2101184 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -336,6 +336,7 @@ include("uuid.jl") include("loading.jl") # misc useful functions & macros +include("random.jl") include("timing.jl") include("util.jl") diff --git a/base/random.jl b/base/random.jl new file mode 100644 index 0000000000000..7b75b61419c04 --- /dev/null +++ b/base/random.jl @@ -0,0 +1,66 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Base.CoreRandom + +CoreRandom defines a random number generator by wrapping the API provided by +the operating system. It is an internal module used primary by `Base`. +Standard library `Random` re-exposes the implementations defined here for external +use. +""" +module CoreRandom + +using Base: BitInteger + +# Functions local to `CoreRandom` and separated from `Random`. +rand(::Type{T}) where {T} = rand(RandomDevice(), T) +rand!(A::Array) = rand!(RandomDevice(), A) + +# Note that CoreRandom.RandomDevice != Random.RandomDevice +if Sys.iswindows() + struct RandomDevice + buffer::Vector{UInt128} + + RandomDevice() = new(Vector{UInt128}(undef, 1)) + end + + function rand(rd::RandomDevice, ::Type{T}) where {T<:Union{Bool, BitInteger}} + rand!(rd, rd.buffer) + @inbounds return rd.buffer[1] % T + end +else # !windows + struct RandomDevice + unlimited::Bool + + RandomDevice(; unlimited::Bool=true) = new(unlimited) + end + + rand(rd::RandomDevice, ::Type{T}) where {T<:BitInteger} = read(getfile(rd), T) + rand(rd::RandomDevice, ::Type{Bool}) = read(getfile(rd), UInt8) % Bool + + function getfile(rd::RandomDevice) + devrandom = rd.unlimited ? DEV_URANDOM : DEV_RANDOM + # TODO: there is a data-race, this can leak up to nthreads() copies of the file descriptors, + # so use a "thread-once" utility once available + isassigned(devrandom) || (devrandom[] = open(rd.unlimited ? "/dev/urandom" : "/dev/random")) + devrandom[] + end + + const DEV_RANDOM = Ref{IOStream}() + const DEV_URANDOM = Ref{IOStream}() + +end # os-test + +# NOTE: this can't be put within the if-else block above +if Sys.iswindows() + function rand!(rd::RandomDevice, A::Array) + Base.windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( + (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), + A, sizeof(A))) + A + end +else + rand!(rd::RandomDevice, A::Array) = read!(getfile(rd), A) +end + +end # CoreRandom diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 3ac2c3394a51c..1e6add2efcfac 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -6,57 +6,6 @@ SamplerUnion(U...) = Union{Any[SamplerType{T} for T in U]...} const SamplerBoolBitInteger = SamplerUnion(Bool, BitInteger_types...) -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 -else # !windows - struct RandomDevice <: AbstractRNG - unlimited::Bool - - RandomDevice(; unlimited::Bool=true) = new(unlimited) - end - - rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[]) - rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool - - function getfile(rd::RandomDevice) - devrandom = rd.unlimited ? DEV_URANDOM : DEV_RANDOM - # TODO: there is a data-race, this can leak up to nthreads() copies of the file descriptors, - # so use a "thread-once" utility once available - isassigned(devrandom) || (devrandom[] = open(rd.unlimited ? "/dev/urandom" : "/dev/random")) - devrandom[] - end - - const DEV_RANDOM = Ref{IOStream}() - const DEV_URANDOM = Ref{IOStream}() - -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))) - 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() @@ -64,9 +13,21 @@ 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 +struct RandomDevice <: AbstractRNG + impl::Base.CoreRandom.RandomDevice + RandomDevice(; kwargs...) = new(Base.CoreRandom.RandomDevice(; kwargs...)) +end RandomDevice(::Nothing) = RandomDevice() + +# Interface `Base.CoreRandom`: +rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = Base.CoreRandom.rand(rd.impl, sp[]) +rand!(rd::RandomDevice, A::Array{T}, ::SamplerType{T}) where {T <: Union{Bool, BitInteger}} = + Base.CoreRandom.rand!(rd.impl, A) + +# RandomDevice produces natively UInt64 +rng_native_52(::RandomDevice) = UInt64 + seed!(rng::RandomDevice) = rng