From 39a69d323da95ce642ea7fe8d40eb8bdd6a277c8 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 22 Sep 2013 20:51:57 +1000 Subject: std::rand: Add OSRng, ReaderRng wrappers around the OS RNG & generic Readers respectively. The former reads from e.g. /dev/urandom, the latter just wraps any std::rt::io::Reader into an interface that implements Rng. This also adds Rng.fill_bytes for efficient implementations of the above (reading 8 bytes at a time is inefficient when you can read 1000), and removes the dependence on src/rt (i.e. rand_gen_seed) although this last one requires implementing hand-seeding of the XorShiftRng used in the scheduler on Linux/unixes, since OSRng relies on a scheduler existing to be able to read from /dev/urandom. --- src/libstd/rt/sched.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index cbffec51cc9..004dab8d73a 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -140,7 +140,7 @@ impl Scheduler { cleanup_job: None, run_anything: run_anything, friend_handle: friend, - rng: XorShiftRng::new(), + rng: new_sched_rng(), idle_callback: None, yield_check_count: 0, steal_for_yield: false @@ -844,6 +844,60 @@ impl ClosureConverter for UnsafeTaskReceiver { fn to_fn(self) -> &fn(&mut Scheduler, ~Task) { unsafe { transmute(self) } } } +// On unix, we read randomness straight from /dev/urandom, but the +// default constructor of an XorShiftRng does this via io::file, which +// relies on the scheduler existing, so we have to manually load +// randomness. Windows has its own C API for this, so we don't need to +// worry there. +#[cfg(windows)] +fn new_sched_rng() -> XorShiftRng { + XorShiftRng::new() +} +#[cfg(unix)] +#[fixed_stack_segment] #[inline(never)] +fn new_sched_rng() -> XorShiftRng { + use libc; + use sys; + use c_str::ToCStr; + use ptr::RawPtr; + use vec::MutableVector; + use iter::Iterator; + + // XXX: this could use io::native::file, when it works. + let file = do "/dev/urandom".with_c_str |name| { + do "r".with_c_str |mode| { + unsafe { libc::fopen(name, mode) } + } + }; + if file.is_null() { + rtabort!("could not open /dev/urandom for reading.") + } + + let mut seeds = [0u32, .. 4]; + loop { + let nbytes = do seeds.as_mut_buf |buf, len| { + unsafe { + libc::fread(buf as *mut libc::c_void, + sys::size_of::() as libc::size_t, + len as libc::size_t, + file) + } + }; + rtassert!(nbytes == seeds.len() as libc::size_t); + + if !seeds.iter().all(|x| *x == 0) { + break; + } + } + + // XXX: do we need to guarantee that this is closed with a finally + // block (is that even possible without a scheduler?), or do we + // know that the only way that we can fail here is `abort`ing? + unsafe {libc::fclose(file);} + + XorShiftRng::new_seeded(seeds[0], seeds[1], seeds[2], seeds[3]) +} + #[cfg(test)] mod test { extern mod extra; -- cgit 1.4.1-3-g733a5