@@ -530,32 +530,53 @@ mod imp {
530
530
use std:: fs:: File ;
531
531
use std:: io:: Read ;
532
532
use std:: io:: ErrorKind :: * ;
533
+ use std:: sync:: { Once , Mutex , ONCE_INIT } ;
533
534
534
535
#[ derive( Debug ) ]
535
- pub struct OsRng {
536
- dev_random : File ,
537
- }
536
+ pub struct OsRng ( ) ;
537
+
538
+ // TODO: remove outer Option when `Mutex::new(None)` is a constant expression
539
+ static mut READ_RNG_FILE : Option < Mutex < Option < File > > > = None ;
540
+ static READ_RNG_ONCE : Once = ONCE_INIT ;
538
541
539
542
impl OsRng {
540
543
pub fn new ( ) -> Result < OsRng , Error > {
541
- let dev_random = File :: open ( "rand:" ) . map_err ( |err| {
542
- match err. kind ( ) {
543
- Interrupted => Error :: new ( ErrorKind :: Transient , "interrupted" ) ,
544
- WouldBlock => Error :: with_cause ( ErrorKind :: NotReady ,
545
- "opening random device would block" , err) ,
546
- _ => Error :: with_cause ( ErrorKind :: Unavailable ,
547
- "error while opening random device" , err)
548
- }
549
- } ) ?;
550
- Ok ( OsRng { dev_random : dev_random } )
544
+ READ_RNG_ONCE . call_once ( || {
545
+ unsafe { READ_RNG_FILE = Some ( Mutex :: new ( None ) ) }
546
+ } ) ;
547
+
548
+ // We try opening the file outside the `call_once` fn because we cannot
549
+ // clone the error, thus we must retry on failure.
550
+
551
+ let mutex = unsafe { READ_RNG_FILE . as_ref ( ) . unwrap ( ) } ;
552
+ let mut guard = mutex. lock ( ) . unwrap ( ) ;
553
+ if ( * guard) . is_none ( ) {
554
+ info ! ( "OsRng: opening random device 'rand:'" ) ;
555
+ let file = File :: open ( "rand:" ) . map_err ( |err| {
556
+ match err. kind ( ) {
557
+ Interrupted => Error :: new ( ErrorKind :: Transient , "interrupted" ) ,
558
+ WouldBlock => Error :: with_cause ( ErrorKind :: NotReady ,
559
+ "opening random device would block" , err) ,
560
+ _ => Error :: with_cause ( ErrorKind :: Unavailable ,
561
+ "error while opening random device" , err)
562
+ }
563
+ } ) ?;
564
+ * guard = Some ( file) ;
565
+ } ;
566
+ Ok ( OsRng ( ) )
551
567
}
552
568
553
569
pub fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
554
570
if dest. len ( ) == 0 { return Ok ( ( ) ) ; }
555
571
trace ! ( "OsRng: reading {} bytes from random device" , dest. len( ) ) ;
556
572
573
+ // Since we have an instance of Self, we can assume that our memory was
574
+ // set with a valid object.
575
+ let mutex = unsafe { READ_RNG_FILE . as_ref ( ) . unwrap ( ) } ;
576
+ let mut guard = mutex. lock ( ) . unwrap ( ) ;
577
+ let file = ( * guard) . as_mut ( ) . unwrap ( ) ;
557
578
// Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`.
558
- self . dev_random . read_exact ( dest) . map_err ( |err| {
579
+ file . read_exact ( dest) . map_err ( |err| {
559
580
Error :: with_cause ( ErrorKind :: Unavailable ,
560
581
"error reading random device" , err)
561
582
} )
0 commit comments