diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index cffca3e882f..f8215213c87 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -21,6 +21,7 @@ use core::intrinsics::transmute; use core::slice; +use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use Rng; /// Implement `next_u64` via `next_u32`, little-endian order. @@ -109,4 +110,39 @@ pub fn next_u128_via_fill(rng: &mut R) -> u128 { impl_uint_from_fill!(rng, u128, 16) } +const LIMIT_ERR_MAX: usize = 20; // arbitrary +static LIMIT_ERR_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; + +/// Implement `fill_bytes` via `try_fill` with implicit error handling. +pub fn fill_via_try_fill(rng: &mut R, dest: &mut [u8]) { + loop { + if let Err(e) = rng.try_fill(dest) { + if e.kind.should_retry() { + if e.kind.limit_retries() { + // We use a global counter since we don't have any local memory. + let count = LIMIT_ERR_COUNT.fetch_add(1, Ordering::Relaxed); + if count > LIMIT_ERR_MAX { + // TODO: log details & cause? + panic!("Too many RNG errors; last error: {}", e.msg()); + } + } + + if e.kind.should_wait() { + #[cfg(feature="std")]{ + let dur = ::std::time::Duration::from_millis(10); + ::std::thread::sleep(dur); + } + } + + continue; + } + + // TODO: log details & cause? + panic!("Fatal RNG error: {}", e.msg()); + } + + break; + } +} + // TODO: implement tests for the above diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index d5a9ab0b321..a10d735c669 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -260,15 +260,19 @@ impl ErrorKind { _ => false, } } + /// True if we should retry but wait before retrying /// /// This implies `should_retry()` is true. pub fn should_wait(self) -> bool { - match self { - ErrorKind::NotReady => true, - _ => false, - } + self == ErrorKind::NotReady } + + /// True if we should limit the number of retries before giving up. + pub fn limit_retries(self) -> bool { + self == ErrorKind::Transient + } + /// A description of this error kind pub fn description(self) -> &'static str { match self { diff --git a/src/os.rs b/src/os.rs index e65d770af1e..34958cf8597 100644 --- a/src/os.rs +++ b/src/os.rs @@ -68,7 +68,7 @@ impl Rng for OsRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill(dest).unwrap(); + ::rand_core::impls::fill_via_try_fill(self, dest) } fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { @@ -152,7 +152,7 @@ mod imp { continue; } else { return Err(Error::new_with_cause( - ErrorKind::Other, + ErrorKind::Unavailable, "unexpected getrandom error", err, )); diff --git a/src/read.rs b/src/read.rs index acc905b7010..0169f53f80b 100644 --- a/src/read.rs +++ b/src/read.rs @@ -62,7 +62,7 @@ impl Rng for ReadRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.try_fill(dest).unwrap(); + ::rand_core::impls::fill_via_try_fill(self, dest) } fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {