From d0f06ad2c2e94a5c8273ac193811dfb979268ca0 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Sat, 30 Dec 2023 14:32:47 +0000 Subject: [PATCH 1/2] benches/generators.rs: standardize thread_rng benchmarks --- benches/generators.rs | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/benches/generators.rs b/benches/generators.rs index 96fa302b6a0..bf8573f76cf 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -52,6 +52,7 @@ 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()); macro_rules! gen_uint { ($fnn:ident, $ty:ty, $gen:expr) => { @@ -82,6 +83,7 @@ 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_u64_step, u64, StepRng::new(0, 1)); gen_uint!(gen_u64_pcg32, u64, Pcg32::from_entropy()); @@ -95,6 +97,7 @@ 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()); macro_rules! init_gen { ($fnn:ident, $gen:ident) => { @@ -141,24 +144,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); From 7691dd468e1f3bb5850f834946f025c394adfc20 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 30 Jan 2024 17:37:13 +0000 Subject: [PATCH 2/2] Add GlobalRng --- benches/generators.rs | 4 ++ src/lib.rs | 2 + src/rngs/global.rs | 110 ++++++++++++++++++++++++++++++++++++++++++ src/rngs/mod.rs | 2 + 4 files changed, 118 insertions(+) create mode 100644 src/rngs/global.rs diff --git a/benches/generators.rs b/benches/generators.rs index bf8573f76cf..81c308d0a4a 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -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}; @@ -53,6 +54,7 @@ gen_bytes!(gen_bytes_std, StdRng::from_entropy()); 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) => { @@ -84,6 +86,7 @@ gen_uint!(gen_u32_std, u32, StdRng::from_entropy()); 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()); @@ -98,6 +101,7 @@ gen_uint!(gen_u64_std, u64, StdRng::from_entropy()); 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) => { diff --git a/src/lib.rs b/src/lib.rs index 8ade2881d5d..d44c801e9b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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"))] diff --git a/src/rngs/global.rs b/src/rngs/global.rs new file mode 100644 index 00000000000..84aa5eb534c --- /dev/null +++ b/src/rngs/global.rs @@ -0,0 +1,110 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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>>, +} + +/// 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>> = 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::(); + 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 { .. }"); + } +} diff --git a/src/rngs/mod.rs b/src/rngs/mod.rs index 9013c57d10c..3084cebd7da 100644 --- a/src/rngs/mod.rs +++ b/src/rngs/mod.rs @@ -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;