Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] investigate solutions to immediate-reseeding-on-fork #1377

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 7 additions & 21 deletions benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const BYTES_LEN: usize = 1024;
use core::mem::size_of;
use test::{black_box, Bencher};

use rand::global_rng;
use rand::prelude::*;
use rand::rngs::adapter::ReseedingRng;
use rand::rngs::{mock::StepRng, OsRng};
Expand Down Expand Up @@ -52,6 +53,8 @@ gen_bytes!(gen_bytes_std, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_bytes!(gen_bytes_small, SmallRng::from_entropy());
gen_bytes!(gen_bytes_os, OsRng);
gen_bytes!(gen_bytes_thread, thread_rng());
gen_bytes!(gen_bytes_global, global_rng());

macro_rules! gen_uint {
($fnn:ident, $ty:ty, $gen:expr) => {
Expand Down Expand Up @@ -82,6 +85,8 @@ gen_uint!(gen_u32_std, u32, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_uint!(gen_u32_small, u32, SmallRng::from_entropy());
gen_uint!(gen_u32_os, u32, OsRng);
gen_uint!(gen_u32_thread, u32, thread_rng());
gen_uint!(gen_u32_global, u32, global_rng());

gen_uint!(gen_u64_step, u64, StepRng::new(0, 1));
gen_uint!(gen_u64_pcg32, u64, Pcg32::from_entropy());
Expand All @@ -95,6 +100,8 @@ gen_uint!(gen_u64_std, u64, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_uint!(gen_u64_small, u64, SmallRng::from_entropy());
gen_uint!(gen_u64_os, u64, OsRng);
gen_uint!(gen_u64_thread, u64, thread_rng());
gen_uint!(gen_u64_global, u64, global_rng());

macro_rules! init_gen {
($fnn:ident, $gen:ident) => {
Expand Down Expand Up @@ -141,24 +148,3 @@ reseeding_bytes!(reseeding_chacha20_32k, 32);
reseeding_bytes!(reseeding_chacha20_64k, 64);
reseeding_bytes!(reseeding_chacha20_256k, 256);
reseeding_bytes!(reseeding_chacha20_1M, 1024);


macro_rules! threadrng_uint {
($fnn:ident, $ty:ty) => {
#[bench]
fn $fnn(b: &mut Bencher) {
let mut rng = thread_rng();
b.iter(|| {
let mut accum: $ty = 0;
for _ in 0..RAND_BENCH_N {
accum = accum.wrapping_add(rng.gen::<$ty>());
}
accum
});
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
}
};
}

threadrng_uint!(thread_rng_u32, u32);
threadrng_uint!(thread_rng_u64, u64);
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ pub mod seq;
// Public exports
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
pub use crate::rngs::thread::thread_rng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
pub use crate::rngs::global::global_rng;
pub use rng::{Fill, Rng};

#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
Expand Down
110 changes: 110 additions & 0 deletions src/rngs/global.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Global random number generator

use std::fmt;
use std::sync::{Mutex, MutexGuard};

use super::std::Core;
use crate::rngs::adapter::ReseedingRng;
use crate::rngs::OsRng;
use crate::{CryptoRng, Error, RngCore, SeedableRng};

// Number of generated bytes after which to reseed `GlobalRng`.
const GLOBAL_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;

/// A reference to the global generator
///
/// This locks the global generator until dropped.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))))]
pub struct GlobalRng<'a> {
lock: MutexGuard<'a, Option<ReseedingRng<Core, OsRng>>>,
}

/// Debug implementation does not leak internal state
impl<'a> fmt::Debug for GlobalRng<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "GlobalRng {{ .. }}")
}
}

static GLOBAL_RNG_KEY: Mutex<Option<ReseedingRng<Core, OsRng>>> = Mutex::new(None);

/// Access the global generator
///
/// Locks until release
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))))]
pub fn global_rng<'a>() -> GlobalRng<'a> {
let mut lock = GLOBAL_RNG_KEY.lock().unwrap();

if lock.is_none() {
let r = Core::from_rng(OsRng).unwrap_or_else(|err|
panic!("could not initialize global_rng: {}", err));
let rng = ReseedingRng::new(r, GLOBAL_RNG_RESEED_THRESHOLD, OsRng);
*lock = Some(rng);
}

assert!(lock.is_some());
GlobalRng { lock }
}

// impl<'a> Default for GlobalRng<'a> {
// fn default() -> GlobalRng {
// global_rng()
// }
// }

impl<'a> RngCore for GlobalRng<'a> {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.next_u32()
}

#[inline(always)]
fn next_u64(&mut self) -> u64 {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.next_u64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.fill_bytes(dest)
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.try_fill_bytes(dest)
}
}

impl<'a> CryptoRng for GlobalRng<'a> {}


#[cfg(test)]
mod test {
#[test]
fn test_global_rng() {
use crate::Rng;
let mut r = crate::global_rng();
r.gen::<i32>();
assert_eq!(r.gen_range(0..1), 0);
}

#[test]
fn test_debug_output() {
// We don't care about the exact output here, but it must not include
// private CSPRNG state or the cache stored by BlockRng!
assert_eq!(std::format!("{:?}", crate::global_rng()), "GlobalRng { .. }");
}
}
2 changes: 2 additions & 0 deletions src/rngs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ mod xoshiro128plusplus;

#[cfg(feature = "std_rng")] mod std;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub(crate) mod thread;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub(crate) mod global;

#[cfg(feature = "small_rng")] pub use self::small::SmallRng;
#[cfg(feature = "std_rng")] pub use self::std::StdRng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub use self::thread::ThreadRng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub use self::global::GlobalRng;

#[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))]
#[cfg(feature = "getrandom")] pub use rand_core::OsRng;
Loading