Skip to content

Commit cfd28c2

Browse files
committed
Merge branch 'master' into log-display
2 parents 14108fc + 33f1516 commit cfd28c2

File tree

6 files changed

+202
-112
lines changed

6 files changed

+202
-112
lines changed

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ extern crate rand;
2525

2626
### Versions
2727

28-
The `rand` crate has been at version `0.3` since March 2015. If you wish to
29-
avoid all breaking changes you may wish to stick with this version.
30-
3128
Version `0.4`was released in December 2017. It contains almost no breaking
3229
changes since the `0.3` series, but nevertheless contains some significant
3330
new code, including a new "external" entropy source (`JitterRng`) and `no_std`

rand-derive/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Add this to your `Cargo.toml`:
99

1010
```toml
1111
[dependencies]
12-
rand = "0.3"
12+
rand = "0.4"
1313
rand_macros = "0.2"
1414
```
1515

src/jitter.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,12 @@ impl Rng for JitterRng {
742742
}
743743

744744
fn fill_bytes(&mut self, dest: &mut [u8]) {
745-
impls::fill_bytes_via_u64(self, dest)
745+
// Fill using `next_u32`. This is faster for filling small slices (four
746+
// bytes or less), while the overhead is negligible.
747+
//
748+
// This is done especially for wrappers that implement `next_u32`
749+
// themselves via `fill_bytes`.
750+
impls::fill_bytes_via_u32(self, dest)
746751
}
747752
}
748753

src/lib.rs

Lines changed: 131 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@
239239
240240
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
241241
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
242-
html_root_url = "https://docs.rs/rand/0.3")]
242+
html_root_url = "https://docs.rs/rand/0.4")]
243243

244244
#![deny(missing_debug_implementations)]
245245

@@ -259,6 +259,7 @@
259259
#[cfg(not(feature = "log"))] macro_rules! debug { ($($x:tt)*) => () }
260260
#[cfg(all(feature="std", not(feature = "log")))] macro_rules! info { ($($x:tt)*) => () }
261261
#[cfg(all(feature="std", not(feature = "log")))] macro_rules! warn { ($($x:tt)*) => () }
262+
#[cfg(all(feature="std", not(feature = "log")))] macro_rules! error { ($($x:tt)*) => () }
262263

263264

264265
use core::{marker, mem};
@@ -287,7 +288,7 @@ use prng::Isaac64Rng as IsaacWordRng;
287288

288289
use distributions::{Range, IndependentSample};
289290
use distributions::range::SampleRange;
290-
#[cfg(feature="std")] use reseeding::{ReseedingRng, ReseedWithNew};
291+
#[cfg(feature="std")] use reseeding::ReseedingRng;
291292

292293
// public modules
293294
pub mod distributions;
@@ -843,29 +844,7 @@ pub trait NewRng: SeedableRng {
843844
#[cfg(feature="std")]
844845
impl<R: SeedableRng> NewRng for R {
845846
fn new() -> Result<Self, Error> {
846-
// Note: error handling would be easier with try/catch blocks
847-
fn new_os<T: SeedableRng>() -> Result<T, Error> {
848-
let mut r = OsRng::new()?;
849-
T::from_rng(&mut r)
850-
}
851-
852-
fn new_jitter<T: SeedableRng>() -> Result<T, Error> {
853-
let mut r = JitterRng::new()?;
854-
T::from_rng(&mut r)
855-
}
856-
857-
trace!("Seeding new RNG");
858-
new_os().or_else(|e1| {
859-
warn!("OsRng failed [falling back to JitterRng]: {}", e1);
860-
new_jitter().map_err(|_e2| {
861-
warn!("JitterRng failed: {}", _e2);
862-
// TODO: can we somehow return both error sources?
863-
Error::with_cause(
864-
ErrorKind::Unavailable,
865-
"seeding a new RNG failed: both OS and Jitter entropy sources failed",
866-
e1)
867-
})
868-
})
847+
R::from_rng(EntropyRng::new())
869848
}
870849
}
871850

@@ -963,20 +942,19 @@ pub fn weak_rng() -> XorShiftRng {
963942
#[cfg(feature="std")]
964943
#[derive(Clone, Debug)]
965944
pub struct ThreadRng {
966-
rng: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>>,
945+
rng: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>>,
967946
}
968947

969948
#[cfg(feature="std")]
970949
thread_local!(
971-
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, ReseedWithNew>>> = {
950+
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<StdRng, EntropyRng>>> = {
972951
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768;
973-
let r = match StdRng::new() {
974-
Ok(r) => r,
975-
Err(e) => panic!("could not initialize thread_rng: {:?}", e)
976-
};
952+
let mut entropy_source = EntropyRng::new();
953+
let r = StdRng::from_rng(&mut entropy_source)
954+
.expect("could not initialize thread_rng");
977955
let rng = ReseedingRng::new(r,
978956
THREAD_RNG_RESEED_THRESHOLD,
979-
ReseedWithNew);
957+
entropy_source);
980958
Rc::new(RefCell::new(rng))
981959
}
982960
);
@@ -1017,6 +995,127 @@ impl Rng for ThreadRng {
1017995
}
1018996
}
1019997

998+
/// An RNG provided specifically for seeding PRNG's.
999+
///
1000+
/// `EntropyRng` uses the interface for random numbers provided by the operating
1001+
/// system ([`OsRng`]). If that returns an error, it will fall back to the
1002+
/// [`JitterRng`] entropy collector. Every time it will then check if `OsRng`
1003+
/// is still not available, and switch back if possible.
1004+
///
1005+
/// [`OsRng`]: os/struct.OsRng.html
1006+
/// [`JitterRng`]: jitter/struct.JitterRng.html
1007+
#[cfg(feature="std")]
1008+
#[derive(Debug)]
1009+
pub struct EntropyRng {
1010+
rng: EntropySource,
1011+
}
1012+
1013+
#[cfg(feature="std")]
1014+
#[derive(Debug)]
1015+
enum EntropySource {
1016+
Os(OsRng),
1017+
Jitter(JitterRng),
1018+
None,
1019+
}
1020+
1021+
#[cfg(feature="std")]
1022+
impl EntropyRng {
1023+
/// Create a new `EntropyRng`.
1024+
///
1025+
/// This method will do no system calls or other initialization routines,
1026+
/// those are done on first use. This is done to make `new` infallible,
1027+
/// and `try_fill_bytes` the only place to report errors.
1028+
pub fn new() -> Self {
1029+
EntropyRng { rng: EntropySource::None }
1030+
}
1031+
}
1032+
1033+
#[cfg(feature="std")]
1034+
impl Rng for EntropyRng {
1035+
fn next_u32(&mut self) -> u32 {
1036+
impls::next_u32_via_fill(self)
1037+
}
1038+
1039+
fn next_u64(&mut self) -> u64 {
1040+
impls::next_u64_via_fill(self)
1041+
}
1042+
1043+
fn fill_bytes(&mut self, dest: &mut [u8]) {
1044+
self.try_fill_bytes(dest).unwrap();
1045+
}
1046+
1047+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
1048+
fn try_os_new(dest: &mut [u8]) -> Result<OsRng, Error>
1049+
{
1050+
let mut rng = OsRng::new()?;
1051+
rng.try_fill_bytes(dest)?;
1052+
Ok(rng)
1053+
}
1054+
1055+
fn try_jitter_new(dest: &mut [u8]) -> Result<JitterRng, Error>
1056+
{
1057+
let mut rng = JitterRng::new()?;
1058+
rng.try_fill_bytes(dest)?;
1059+
Ok(rng)
1060+
}
1061+
1062+
let mut switch_rng = None;
1063+
match self.rng {
1064+
EntropySource::None => {
1065+
let os_rng_result = try_os_new(dest);
1066+
match os_rng_result {
1067+
Ok(os_rng) => {
1068+
switch_rng = Some(EntropySource::Os(os_rng));
1069+
}
1070+
Err(os_rng_error) => {
1071+
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
1072+
os_rng_error);
1073+
match try_jitter_new(dest) {
1074+
Ok(jitter_rng) => {
1075+
switch_rng = Some(EntropySource::Jitter(jitter_rng));
1076+
}
1077+
Err(_jitter_error) => {
1078+
warn!("EntropyRng: JitterRng failed: {}",
1079+
_jitter_error);
1080+
return Err(os_rng_error);
1081+
}
1082+
}
1083+
}
1084+
}
1085+
}
1086+
EntropySource::Os(ref mut rng) => {
1087+
let os_rng_result = rng.try_fill_bytes(dest);
1088+
if let Err(os_rng_error) = os_rng_result {
1089+
warn!("EntropyRng: OsRng failed [falling back to JitterRng]: {}",
1090+
os_rng_error);
1091+
match try_jitter_new(dest) {
1092+
Ok(jitter_rng) => {
1093+
switch_rng = Some(EntropySource::Jitter(jitter_rng));
1094+
}
1095+
Err(_jitter_error) => {
1096+
warn!("EntropyRng: JitterRng failed: {}",
1097+
_jitter_error);
1098+
return Err(os_rng_error);
1099+
}
1100+
}
1101+
}
1102+
}
1103+
EntropySource::Jitter(ref mut rng) => {
1104+
if let Ok(os_rng) = try_os_new(dest) {
1105+
info!("EntropyRng: OsRng available [switching back from JitterRng]");
1106+
switch_rng = Some(EntropySource::Os(os_rng));
1107+
} else {
1108+
return rng.try_fill_bytes(dest); // use JitterRng
1109+
}
1110+
}
1111+
}
1112+
if let Some(rng) = switch_rng {
1113+
self.rng = rng;
1114+
}
1115+
Ok(())
1116+
}
1117+
}
1118+
10201119
/// Generates a random value using the thread-local random number generator.
10211120
///
10221121
/// `random()` can generate various types of random things, and so may require

src/os.rs

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -64,48 +64,52 @@ impl Rng for OsRng {
6464
}
6565

6666
fn fill_bytes(&mut self, dest: &mut [u8]) {
67+
use std::{time, thread};
68+
6769
// We cannot return Err(..), so we try to handle before panicking.
68-
const WAIT_DUR_MS: u32 = 100; // retry every 100ms
69-
const MAX_WAIT: u32 = (10 * 1000) / WAIT_DUR_MS; // max 10s
70-
const TRANSIENT_STEP: u32 = MAX_WAIT / 8;
70+
const MAX_RETRY_PERIOD: u32 = 10; // max 10s
71+
const WAIT_DUR_MS: u32 = 100; // retry every 100ms
72+
let wait_dur = time::Duration::from_millis(WAIT_DUR_MS as u64);
73+
const RETRY_LIMIT: u32 = (MAX_RETRY_PERIOD * 1000) / WAIT_DUR_MS;
74+
const TRANSIENT_RETRIES: u32 = 8;
7175
let mut err_count = 0;
72-
let mut log_err = 0; // log when err_count >= log_err
73-
76+
let mut error_logged = false;
77+
7478
loop {
7579
if let Err(e) = self.try_fill_bytes(dest) {
76-
if log_err == 0 {
77-
warn!("OsRng failed: {}", e);
80+
if err_count >= RETRY_LIMIT {
81+
error!("OsRng failed too many times; last error: {:?}", e);
82+
panic!("OsRng failed too many times; last error: {:?}", e);
7883
}
79-
80-
if e.kind().should_retry() {
81-
if err_count > MAX_WAIT {
82-
panic!("Too many RNG errors or timeout; last error: {}", e.msg());
83-
}
84-
85-
if e.kind().should_wait() {
86-
err_count += 1;
87-
if err_count >= log_err {
88-
log_err += TRANSIENT_STEP;
89-
warn!("OsRng: waiting and retrying ...");
90-
}
91-
#[cfg(feature="std")]{
92-
let dur = ::std::time::Duration::from_millis(WAIT_DUR_MS as u64);
93-
::std::thread::sleep(dur);
84+
85+
match e.kind() {
86+
ErrorKind::Transient => {
87+
if !error_logged {
88+
warn!("OsRng failed; retrying up to {} times. Error: {:?}",
89+
TRANSIENT_RETRIES, e);
90+
error_logged = true;
9491
}
95-
} else {
96-
err_count += TRANSIENT_STEP;
97-
if err_count >= log_err {
98-
log_err += TRANSIENT_STEP;
99-
warn!("OsRng: retrying ...");
92+
err_count += (RETRY_LIMIT + TRANSIENT_RETRIES - 1)
93+
/ TRANSIENT_RETRIES; // round up
94+
continue;
95+
}
96+
ErrorKind::NotReady => {
97+
if !error_logged {
98+
warn!("OsRng failed; waiting up to {}s and retrying. Error: {:?}",
99+
MAX_RETRY_PERIOD, e);
100+
error_logged = true;
100101
}
102+
err_count += 1;
103+
thread::sleep(wait_dur);
104+
continue;
105+
}
106+
_ => {
107+
error!("OsRng failed: {:?}", e);
108+
panic!("OsRng fatal error: {:?}", e);
101109
}
102-
103-
continue;
104110
}
105-
106-
panic!("Fatal RNG error: {}", e.msg());
107111
}
108-
112+
109113
break;
110114
}
111115
}
@@ -239,7 +243,11 @@ mod imp {
239243
// Potentially this would waste bytes, but since we use
240244
// /dev/urandom blocking only happens if not initialised.
241245
// Also, wasting the bytes in v doesn't matter very much.
242-
return Err(Error::new(ErrorKind::NotReady, "getrandom not ready"));
246+
return Err(Error::with_cause(
247+
ErrorKind::NotReady,
248+
"getrandom not ready",
249+
err,
250+
));
243251
} else {
244252
return Err(Error::with_cause(
245253
ErrorKind::Unavailable,
@@ -338,12 +346,16 @@ mod imp {
338346

339347
pub fn try_fill_bytes(&mut self, v: &mut [u8]) -> Result<(), Error> {
340348
trace!("OsRng: reading {} bytes via cloadabi::random_get", v.len());
341-
if unsafe { cloudabi::random_get(v) } == cloudabi::errno::SUCCESS {
349+
let errno = unsafe { cloudabi::random_get(v) };
350+
if errno == cloudabi::errno::SUCCESS {
342351
Ok(())
343352
} else {
344-
Err(Error::new(
353+
// Cloudlibc provides its own `strerror` implementation so we
354+
// can use `from_raw_os_error` here.
355+
Err(Error::with_cause(
345356
ErrorKind::Unavailable,
346357
"random_get() system call failed",
358+
io::Error::from_raw_os_error(errno),
347359
))
348360
}
349361
}
@@ -421,9 +433,10 @@ mod imp {
421433
ptr::null(), 0)
422434
};
423435
if ret == -1 || s_len != s.len() {
424-
return Err(Error::new(
436+
return Err(Error::with_cause(
425437
ErrorKind::Unavailable,
426-
"kern.arandom sysctl failed"));
438+
"kern.arandom sysctl failed",
439+
io::Error::last_os_error()));
427440
}
428441
}
429442
Ok(())

0 commit comments

Comments
 (0)