@@ -13,10 +13,39 @@ use std::fmt;
13
13
use std:: rc:: Rc ;
14
14
use std:: thread_local;
15
15
16
- pub use rand_core:: { self , CryptoRng , RngCore } ;
16
+ pub use rand_core;
17
17
18
- mod reseeding_rng;
19
- use reseeding_rng:: ReseedingRng ;
18
+ use rand_chacha:: ChaCha12Rng ;
19
+ use rand_core:: { CryptoRng , RngCore , SeedableRng } ;
20
+
21
+ // Number of generated bytes after which to reseed `ThreadRng`.
22
+ // According to benchmarks, reseeding has a noticeable impact with thresholds
23
+ // of 32 kB and less. We choose 64 kB to avoid significant overhead.
24
+ const RESEED_THRESHOLD : isize = 1024 * 64 ;
25
+
26
+ struct InnerState {
27
+ rng : ChaCha12Rng ,
28
+ bytes_until_reseed : isize ,
29
+ }
30
+
31
+ impl InnerState {
32
+ #[ inline( always) ]
33
+ fn reseed ( & mut self ) -> Result < ( ) , rand_core:: getrandom:: Error > {
34
+ self . bytes_until_reseed = RESEED_THRESHOLD ;
35
+ self . rng = ChaCha12Rng :: try_from_os_rng ( ) ?;
36
+ Ok ( ( ) )
37
+ }
38
+
39
+ #[ inline( always) ]
40
+ fn reseed_check ( & mut self , n : isize ) {
41
+ if self . bytes_until_reseed < 0 {
42
+ // If system RNG has failed for some reason, ignore the error
43
+ // and continue to work with the old RNG state.
44
+ let _ = self . reseed ( ) ;
45
+ }
46
+ self . bytes_until_reseed -= n;
47
+ }
48
+ }
20
49
21
50
thread_local ! (
22
51
// We require Rc<..> to avoid premature freeing when ThreadRng is used
@@ -35,11 +64,12 @@ thread_local!(
35
64
// completely under our control. We just have to ensure none of them use
36
65
// `ThreadRng` internally, which is nonsensical anyway. We should also never run
37
66
// `ThreadRng` in destructors of its implementation, which is also nonsensical.
38
- static THREAD_RNG_KEY : Rc <UnsafeCell <ReseedingRng >> = {
39
- match ReseedingRng :: new( ) {
40
- Ok ( rng) => Rc :: new( UnsafeCell :: new( rng) ) ,
41
- Err ( err) => panic!( "could not initialize ThreadRng: {}" , err) ,
42
- }
67
+ static THREAD_RNG_KEY : Rc <UnsafeCell <InnerState >> = {
68
+ let rng = match ChaCha12Rng :: try_from_os_rng( ) {
69
+ Ok ( rng) => rng,
70
+ Err ( err) => panic!( "could not initialize ThreadRng: {err}" ) ,
71
+ } ;
72
+ Rc :: new( UnsafeCell :: new( InnerState { rng, bytes_until_reseed: RESEED_THRESHOLD } ) )
43
73
}
44
74
) ;
45
75
@@ -95,24 +125,17 @@ thread_local!(
95
125
#[ derive( Clone ) ]
96
126
pub struct ThreadRng {
97
127
// Rc is explicitly !Send and !Sync
98
- rng : Rc < UnsafeCell < ReseedingRng > > ,
128
+ rng : Rc < UnsafeCell < InnerState > > ,
99
129
}
100
130
101
131
impl ThreadRng {
102
132
/// Immediately reseed the generator
103
133
///
104
134
/// This discards any remaining random data in the cache.
105
135
pub fn reseed ( & mut self ) -> Result < ( ) , rand_core:: getrandom:: Error > {
106
- // SAFETY: We must make sure to stop using `rng` before anyone else
107
- // creates another mutable reference
108
- let rng = unsafe { & mut * self . rng . get ( ) } ;
109
- rng. reseed ( )
110
- }
111
-
112
- fn get_rng ( & mut self ) -> & mut ReseedingRng {
113
- // SAFETY: We must make sure to stop using `rng` before anyone else
114
- // creates another mutable reference
115
- unsafe { & mut * self . rng . get ( ) }
136
+ // SAFETY: The state is thread-local and the reference does not leak from this method
137
+ let s = unsafe { & mut * self . rng . get ( ) } ;
138
+ s. reseed ( )
116
139
}
117
140
}
118
141
@@ -158,17 +181,28 @@ impl Default for ThreadRng {
158
181
impl RngCore for ThreadRng {
159
182
#[ inline( always) ]
160
183
fn next_u32 ( & mut self ) -> u32 {
161
- self . get_rng ( ) . next_u32 ( )
184
+ // SAFETY: The state is thread-local and the reference does not leak from this method
185
+ let s = unsafe { & mut * self . rng . get ( ) } ;
186
+ s. reseed_check ( core:: mem:: size_of :: < u32 > ( ) as isize ) ;
187
+ s. rng . next_u32 ( )
162
188
}
163
189
164
190
#[ inline( always) ]
165
191
fn next_u64 ( & mut self ) -> u64 {
166
- self . get_rng ( ) . next_u64 ( )
192
+ // SAFETY: The state is thread-local and the reference does not leak from this method
193
+ let s = unsafe { & mut * self . rng . get ( ) } ;
194
+ s. reseed_check ( core:: mem:: size_of :: < u64 > ( ) as isize ) ;
195
+ s. rng . next_u64 ( )
167
196
}
168
197
169
198
#[ inline( always) ]
170
199
fn fill_bytes ( & mut self , dest : & mut [ u8 ] ) {
171
- self . get_rng ( ) . fill_bytes ( dest)
200
+ // SAFETY: The state is thread-local and the reference does not leak from this method
201
+ let s = unsafe { & mut * self . rng . get ( ) } ;
202
+ // Valid allocations can not be bigger than `isize::MAX` bytes,
203
+ // so we can cast length to `isize` without issues.
204
+ s. reseed_check ( dest. len ( ) as isize ) ;
205
+ s. rng . fill_bytes ( dest)
172
206
}
173
207
}
174
208
0 commit comments