13
13
14
14
use std:: fmt;
15
15
use std:: io:: Read ;
16
+ #[ allow( unused) ] use std:: fs:: File ;
17
+ #[ allow( unused) ] use std:: path:: Path ;
18
+ #[ allow( unused) ] use std:: sync:: { Once , Mutex , ONCE_INIT } ;
16
19
17
20
use { Rng , Error , ErrorKind , impls} ;
18
21
@@ -101,15 +104,53 @@ impl Rng for OsRng {
101
104
}
102
105
103
106
// Specialisation of `ReadRng` for our purposes
107
+ //
108
+ // Note: all instances use a single internal file handle
104
109
#[ derive( Debug ) ]
105
- struct ReadRng < R > ( R ) ;
110
+ #[ allow( unused) ] // not used by all targets
111
+ struct ReadRng { }
112
+
113
+ // TODO: remove outer Option when `Mutex::new(None)` is a constant expression
114
+ static mut READ_RNG_FILE : Option < Mutex < Option < File > > > = None ;
115
+ static READ_RNG_ONCE : Once = ONCE_INIT ;
106
116
107
- impl < R : Read > ReadRng < R > {
108
- #[ allow( unused) ] // not used by all targets
117
+ #[ allow( unused) ] // not used by all targets
118
+ impl ReadRng {
119
+ // Open a `File` for the given `path`.
120
+ //
121
+ // Uses a mutex on a static object to limit `OsRng` to a single file descriptor.
122
+ fn open < P : AsRef < Path > > ( path : P ) -> Result < ReadRng , Error > {
123
+ READ_RNG_ONCE . call_once ( || {
124
+ unsafe { READ_RNG_FILE = Some ( Mutex :: new ( None ) ) }
125
+ } ) ;
126
+
127
+ // We try opening the file outside the `call_once` fn because we cannot
128
+ // clone the error, thus we must retry on failure.
129
+
130
+ let mutex = unsafe { READ_RNG_FILE . as_ref ( ) . unwrap ( ) } ;
131
+ let mut guard = mutex. lock ( ) . unwrap ( ) ;
132
+ if ( * guard) . is_none ( ) {
133
+ let file = File :: open ( path) . map_err ( |err| Error :: with_cause (
134
+ ErrorKind :: Unavailable ,
135
+ "error opening random device" ,
136
+ err
137
+ ) ) ?;
138
+ * guard = Some ( file) ;
139
+ }
140
+
141
+ Ok ( ReadRng { } )
142
+ }
143
+
109
144
fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
110
145
if dest. len ( ) == 0 { return Ok ( ( ) ) ; }
146
+
147
+ // Since we have an instance of Self, we can assume that our memory was
148
+ // set with a valid object.
149
+ let mutex = unsafe { READ_RNG_FILE . as_ref ( ) . unwrap ( ) } ;
150
+ let mut guard = mutex. lock ( ) . unwrap ( ) ;
151
+ let mut file = ( * guard) . as_mut ( ) . unwrap ( ) ;
111
152
// Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`.
112
- self . 0 . read_exact ( dest) . map_err ( |err| {
153
+ file . read_exact ( dest) . map_err ( |err| {
113
154
Error :: with_cause ( ErrorKind :: Unavailable , "error reading random device" , err)
114
155
} )
115
156
}
@@ -131,7 +172,6 @@ mod imp {
131
172
use { Error , ErrorKind } ;
132
173
133
174
use std:: io;
134
- use std:: fs:: File ;
135
175
136
176
#[ cfg( all( target_os = "linux" ,
137
177
any( target_arch = "x86_64" ,
@@ -243,7 +283,7 @@ mod imp {
243
283
#[ derive( Debug ) ]
244
284
enum OsRngInner {
245
285
OsGetrandomRng ,
246
- OsReadRng ( ReadRng < File > ) ,
286
+ OsReadRng ( ReadRng ) ,
247
287
}
248
288
249
289
impl OsRng {
@@ -252,11 +292,7 @@ mod imp {
252
292
return Ok ( OsRng { inner : OsGetrandomRng } ) ;
253
293
}
254
294
255
- let reader = File :: open ( "/dev/urandom" ) . map_err ( |err| {
256
- Error :: with_cause ( ErrorKind :: Unavailable , "error opening random device" , err)
257
- } ) ?;
258
- let reader_rng = ReadRng ( reader) ;
259
-
295
+ let reader_rng = ReadRng :: open ( "/dev/urandom" ) ?;
260
296
Ok ( OsRng { inner : OsReadRng ( reader_rng) } )
261
297
}
262
298
@@ -411,23 +447,16 @@ mod imp {
411
447
#[ cfg( target_os = "redox" ) ]
412
448
mod imp {
413
449
use { Error , ErrorKind } ;
414
-
415
- use std:: io;
416
- use std:: fs:: File ;
417
450
use super :: ReadRng ;
418
451
419
452
#[ derive( Debug ) ]
420
453
pub struct OsRng {
421
- inner : ReadRng < File > ,
454
+ inner : ReadRng ,
422
455
}
423
456
424
457
impl OsRng {
425
458
pub fn new ( ) -> Result < OsRng , Error > {
426
- let reader = File :: open ( "rand:" ) . map_err ( |err| {
427
- Error :: with_cause ( ErrorKind :: Unavailable , "error opening random device" , err)
428
- } ) ?;
429
- let reader_rng = ReadRng ( reader) ;
430
-
459
+ let reader_rng = ReadRng :: open ( "rand:" ) ?;
431
460
Ok ( OsRng { inner : reader_rng } )
432
461
}
433
462
pub fn try_fill_bytes ( & mut self , v : & mut [ u8 ] ) -> Result < ( ) , Error > {
0 commit comments