10
10
11
11
//! Thread-local random number generator
12
12
13
- use std:: cell:: RefCell ;
13
+ use std:: cell:: UnsafeCell ;
14
14
use std:: rc:: Rc ;
15
15
16
16
use { RngCore , CryptoRng , SeedableRng , EntropyRng } ;
17
17
use prng:: hc128:: Hc128Core ;
18
18
use { Distribution , Uniform , Rng , Error } ;
19
19
use reseeding:: ReseedingRng ;
20
20
21
+ // Rationale for using `UnsafeCell` in `ThreadRng`:
22
+ //
23
+ // Previously we used a `RefCell`, with an overhead of ~15%. There will only
24
+ // ever be one mutable reference to the interior of the `UnsafeCell`, because
25
+ // we only have such a reference inside `next_u32`, `next_u64`, etc. Within a
26
+ // single thread (which is the definition of `ThreadRng`), there will only ever
27
+ // be one of these methods active at a time.
28
+ //
29
+ // A possible scenario where there could be multiple mutable references is if
30
+ // `ThreadRng` is used inside `next_u32` and co. But the implementation is
31
+ // completely under our control. We just have to ensure none of them use
32
+ // `ThreadRng` internally, which is nonsensical anyway. We should also never run
33
+ // `ThreadRng` in destructors of its implementation, which is also nonsensical.
34
+ //
35
+ // The additional `Rc` is not strictly neccesary, and could be removed. For now
36
+ // it ensures `ThreadRng` stays `!Send` and `!Sync`, and implements `Clone`.
37
+
38
+
21
39
// Number of generated bytes after which to reseed `TreadRng`.
22
40
//
23
41
// The time it takes to reseed HC-128 is roughly equivalent to generating 7 KiB.
@@ -32,18 +50,18 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB
32
50
/// [`thread_rng`]: fn.thread_rng.html
33
51
#[ derive( Clone , Debug ) ]
34
52
pub struct ThreadRng {
35
- rng : Rc < RefCell < ReseedingRng < Hc128Core , EntropyRng > > > ,
53
+ rng : Rc < UnsafeCell < ReseedingRng < Hc128Core , EntropyRng > > > ,
36
54
}
37
55
38
56
thread_local ! (
39
- static THREAD_RNG_KEY : Rc <RefCell <ReseedingRng <Hc128Core , EntropyRng >>> = {
57
+ static THREAD_RNG_KEY : Rc <UnsafeCell <ReseedingRng <Hc128Core , EntropyRng >>> = {
40
58
let mut entropy_source = EntropyRng :: new( ) ;
41
59
let r = Hc128Core :: from_rng( & mut entropy_source) . unwrap_or_else( |err|
42
60
panic!( "could not initialize thread_rng: {}" , err) ) ;
43
61
let rng = ReseedingRng :: new( r,
44
62
THREAD_RNG_RESEED_THRESHOLD ,
45
63
entropy_source) ;
46
- Rc :: new( RefCell :: new( rng) )
64
+ Rc :: new( UnsafeCell :: new( rng) )
47
65
}
48
66
) ;
49
67
@@ -80,20 +98,20 @@ pub fn thread_rng() -> ThreadRng {
80
98
impl RngCore for ThreadRng {
81
99
#[ inline( always) ]
82
100
fn next_u32 ( & mut self ) -> u32 {
83
- self . rng . borrow_mut ( ) . next_u32 ( )
101
+ unsafe { ( * self . rng . get ( ) ) . next_u32 ( ) }
84
102
}
85
103
86
104
#[ inline( always) ]
87
105
fn next_u64 ( & mut self ) -> u64 {
88
- self . rng . borrow_mut ( ) . next_u64 ( )
106
+ unsafe { ( * self . rng . get ( ) ) . next_u64 ( ) }
89
107
}
90
108
91
109
fn fill_bytes ( & mut self , bytes : & mut [ u8 ] ) {
92
- self . rng . borrow_mut ( ) . fill_bytes ( bytes)
110
+ unsafe { ( * self . rng . get ( ) ) . fill_bytes ( bytes) }
93
111
}
94
112
95
113
fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
96
- self . rng . borrow_mut ( ) . try_fill_bytes ( dest)
114
+ unsafe { ( * self . rng . get ( ) ) . try_fill_bytes ( dest) }
97
115
}
98
116
}
99
117
0 commit comments