Skip to content

Commit f5cbc91

Browse files
committed
Use UnsafeCell in ThreadRng
1 parent c3c1eec commit f5cbc91

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

src/thread_rng.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! Thread-local random number generator
1212
13-
use std::cell::RefCell;
13+
use std::cell::UnsafeCell;
1414
use std::rc::Rc;
1515

1616
use {RngCore, CryptoRng, SeedableRng, EntropyRng};
@@ -32,18 +32,18 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB
3232
/// [`thread_rng`]: fn.thread_rng.html
3333
#[derive(Clone, Debug)]
3434
pub struct ThreadRng {
35-
rng: Rc<RefCell<ReseedingRng<Hc128Core, EntropyRng>>>,
35+
rng: Rc<UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>>>,
3636
}
3737

3838
thread_local!(
39-
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<Hc128Core, EntropyRng>>> = {
39+
static THREAD_RNG_KEY: Rc<UnsafeCell<ReseedingRng<Hc128Core, EntropyRng>>> = {
4040
let mut entropy_source = EntropyRng::new();
4141
let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err|
4242
panic!("could not initialize thread_rng: {}", err));
4343
let rng = ReseedingRng::new(r,
4444
THREAD_RNG_RESEED_THRESHOLD,
4545
entropy_source);
46-
Rc::new(RefCell::new(rng))
46+
Rc::new(UnsafeCell::new(rng))
4747
}
4848
);
4949

@@ -77,23 +77,31 @@ pub fn thread_rng() -> ThreadRng {
7777
ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
7878
}
7979

80+
/*
81+
When could we get more then two active mutable references to `thread_rng`?
82+
Normally there are only mutable references to the RNG inside `UnsafeCell` inside `next_u32`, `next_u64` etc. Within a single thread (which is the definition of `ThreadRng`), there will only ever be one of these functions active at a time.
83+
84+
A possible scenario where there could be multiple mutable references is if `ThreadRng` is used _inside_ `next_u32` and co. But the three parts that make up `ThreadRng`, a PRNG, `EntropyRng` and the `ReseedingRng` wrapper are all under our control. We just have to ensure none of them use `ThreadRng` internally, which is nonsensical anyway.
85+
86+
Another possible option might be if we where in the middle of `next_u32` (and co.), another thread panics, in this thread destructors start to run, and the destructors make use of `ThreadRng`. Is this a real problem or does panicking work differently?
87+
*/
8088
impl RngCore for ThreadRng {
8189
#[inline(always)]
8290
fn next_u32(&mut self) -> u32 {
83-
self.rng.borrow_mut().next_u32()
91+
unsafe { (*self.rng.get()).next_u32() }
8492
}
8593

8694
#[inline(always)]
8795
fn next_u64(&mut self) -> u64 {
88-
self.rng.borrow_mut().next_u64()
96+
unsafe { (*self.rng.get()).next_u64() }
8997
}
9098

9199
fn fill_bytes(&mut self, bytes: &mut [u8]) {
92-
self.rng.borrow_mut().fill_bytes(bytes)
100+
unsafe { (*self.rng.get()).fill_bytes(bytes) }
93101
}
94102

95103
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
96-
self.rng.borrow_mut().try_fill_bytes(dest)
104+
unsafe { (*self.rng.get()).try_fill_bytes(dest) }
97105
}
98106
}
99107

0 commit comments

Comments
 (0)