Skip to content

Commit 8a0deb7

Browse files
committed
Separate ThreadRng internal as ThreadRngCore
This commit refactors `ThreadRng` to separate its internal PRNG and reseeding logic as `ThreadRngCore` so the new type could be exposed to public in the future for those who want to copy `ThreadRng` behavior with an independent state or in a Send + Sync context. See also #1748
1 parent ccb734b commit 8a0deb7

1 file changed

Lines changed: 71 additions & 11 deletions

File tree

src/rngs/thread.rs

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,57 @@ impl ReseedingCore {
7474
}
7575
}
7676

77+
/// The [`ThreadRng`] internal
78+
struct ThreadRngCore {
79+
inner: BlockRng<ReseedingCore>,
80+
}
81+
82+
/// Debug implementation does not leak internal state
83+
impl fmt::Debug for ThreadRngCore {
84+
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
85+
write!(fmt, "ThreadRngCore {{ .. }}")
86+
}
87+
}
88+
89+
impl ThreadRngCore {
90+
/// Initialize the generator using [`SysRng`]
91+
fn new() -> Result<Self, SysError> {
92+
Core::try_from_rng(&mut SysRng).map(|result| Self {
93+
inner: BlockRng::new(ReseedingCore { inner: result }),
94+
})
95+
}
96+
97+
/// Immediately reseed the generator
98+
///
99+
/// This discards any remaining random data in the cache.
100+
fn reseed(&mut self) -> Result<(), SysError> {
101+
self.inner.reset_and_skip(0);
102+
self.inner.core.reseed()
103+
}
104+
}
105+
106+
impl TryRng for ThreadRngCore {
107+
type Error = Infallible;
108+
109+
#[inline(always)]
110+
fn try_next_u32(&mut self) -> Result<u32, Infallible> {
111+
Ok(self.inner.next_word())
112+
}
113+
114+
#[inline(always)]
115+
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
116+
Ok(self.inner.next_u64_from_u32())
117+
}
118+
119+
#[inline(always)]
120+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
121+
self.inner.fill_bytes(dest);
122+
Ok(())
123+
}
124+
}
125+
126+
impl TryCryptoRng for ThreadRngCore {}
127+
77128
/// A reference to the thread-local generator
78129
///
79130
/// This type is a reference to a lazily-initialized thread-local generator.
@@ -127,7 +178,7 @@ impl ReseedingCore {
127178
#[derive(Clone)]
128179
pub struct ThreadRng {
129180
// Rc is explicitly !Send and !Sync
130-
rng: Rc<UnsafeCell<BlockRng<ReseedingCore>>>,
181+
rng: Rc<UnsafeCell<ThreadRngCore>>,
131182
}
132183

133184
impl ThreadRng {
@@ -138,8 +189,7 @@ impl ThreadRng {
138189
// SAFETY: We must make sure to stop using `rng` before anyone else
139190
// creates another mutable reference
140191
let rng = unsafe { &mut *self.rng.get() };
141-
rng.reset_and_skip(0);
142-
rng.core.reseed()
192+
rng.reseed()
143193
}
144194
}
145195

@@ -153,12 +203,11 @@ impl fmt::Debug for ThreadRng {
153203
thread_local!(
154204
// We require Rc<..> to avoid premature freeing when ThreadRng is used
155205
// within thread-local destructors. See #968.
156-
static THREAD_RNG_KEY: Rc<UnsafeCell<BlockRng<ReseedingCore>>> = {
157-
Rc::new(UnsafeCell::new(BlockRng::new(ReseedingCore {
158-
inner: Core::try_from_rng(&mut SysRng).unwrap_or_else(|err| {
206+
static THREAD_RNG_KEY: Rc<UnsafeCell<ThreadRngCore>> = {
207+
Rc::new(UnsafeCell::new(ThreadRngCore::new().unwrap_or_else(|err| {
159208
panic!("could not initialize ThreadRng: {}", err)
160209
}),
161-
})))
210+
))
162211
}
163212
);
164213

@@ -209,24 +258,23 @@ impl TryRng for ThreadRng {
209258
// SAFETY: We must make sure to stop using `rng` before anyone else
210259
// creates another mutable reference
211260
let rng = unsafe { &mut *self.rng.get() };
212-
Ok(rng.next_word())
261+
rng.try_next_u32()
213262
}
214263

215264
#[inline(always)]
216265
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
217266
// SAFETY: We must make sure to stop using `rng` before anyone else
218267
// creates another mutable reference
219268
let rng = unsafe { &mut *self.rng.get() };
220-
Ok(rng.next_u64_from_u32())
269+
rng.try_next_u64()
221270
}
222271

223272
#[inline(always)]
224273
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Infallible> {
225274
// SAFETY: We must make sure to stop using `rng` before anyone else
226275
// creates another mutable reference
227276
let rng = unsafe { &mut *self.rng.get() };
228-
rng.fill_bytes(dest);
229-
Ok(())
277+
rng.try_fill_bytes(dest)
230278
}
231279
}
232280

@@ -242,10 +290,22 @@ mod test {
242290
assert_eq!(r.random_range(0..1), 0);
243291
}
244292

293+
#[test]
294+
fn test_thread_rng_core() {
295+
use crate::RngExt;
296+
let mut r = super::ThreadRngCore::new().unwrap();
297+
r.random::<i32>();
298+
assert_eq!(r.random_range(0..1), 0);
299+
}
300+
245301
#[test]
246302
fn test_debug_output() {
247303
// We don't care about the exact output here, but it must not include
248304
// private CSPRNG state or the cache stored by BlockRng!
249305
assert_eq!(std::format!("{:?}", crate::rng()), "ThreadRng { .. }");
306+
assert_eq!(
307+
std::format!("{:?}", super::ThreadRngCore::new().unwrap()),
308+
"ThreadRngCore { .. }"
309+
);
250310
}
251311
}

0 commit comments

Comments
 (0)