diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 4f8c7254b4..0378fe9fc6 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -1,10 +1,6 @@ -use std::{ - cell::Cell, - collections::hash_map::DefaultHasher, - hash::Hasher, - num::Wrapping, - sync::atomic::{AtomicUsize, Ordering}, -}; +use core::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "std")] +use std::cell::Cell; // Based on [Fisher–Yates shuffle]. // @@ -18,37 +14,86 @@ pub fn shuffle(slice: &mut [T]) { /// Return a value from `0..n`. fn gen_index(n: usize) -> usize { - (random() % n as u64) as usize + random() % n } -/// Pseudorandom number generator based on [xorshift*]. +/// Pseudorandom number generator based on [xorshift]. /// -/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift* -fn random() -> u64 { - thread_local! { - static RNG: Cell> = Cell::new(Wrapping(prng_seed())); - } - - fn prng_seed() -> u64 { +/// [xorshift]: https://en.wikipedia.org/wiki/Xorshift +fn random() -> usize { + #[cfg(feature = "std")] + fn prng_seed() -> usize { static COUNTER: AtomicUsize = AtomicUsize::new(0); // Any non-zero seed will do let mut seed = 0; while seed == 0 { - let mut hasher = DefaultHasher::new(); - hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed)); - seed = hasher.finish(); + seed = COUNTER.fetch_add(1, Ordering::Relaxed); } seed } - RNG.with(|rng| { - let mut x = rng.get(); - debug_assert_ne!(x.0, 0); + /// [xorshift*] is used on 64bit platforms. + /// + /// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift* + #[cfg(target_pointer_width = "64")] + fn xorshift(mut x: usize) -> (usize, usize) { + debug_assert_ne!(x, 0); x ^= x >> 12; x ^= x << 25; x ^= x >> 27; - rng.set(x); - x.0.wrapping_mul(0x2545_f491_4f6c_dd1d) - }) + ( + x, + x.wrapping_mul(0x2545_f491_4f6c_dd1d), + ) + } + + /// [xorshift32] is used on 32bit platforms. + /// + /// [xorshift32]: https://en.wikipedia.org/wiki/Xorshift + #[cfg(target_pointer_width = "32")] + fn xorshift(mut x: usize) -> (usize, usize) { + debug_assert_ne!(x, 0); + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + (x, x) + } + + /// A non-standard xorshift variant is used on 16bit platforms. + #[cfg(target_pointer_width = "16")] + fn xorshift(mut x: usize) -> (usize, usize) { + // Constants chosen from: http://b2d-f9r.blogspot.com/2010/08/16-bit-xorshift-rng.html + debug_assert_ne!(x, 0); + x ^= x << 4; + x ^= x >> 3; + x ^= x << 7; + (x, x) + } + + #[cfg(feature = "std")] + fn rng() -> usize { + thread_local! { + static RNG: Cell = Cell::new(prng_seed()); + } + + RNG.with(|rng| { + let (x, res) = xorshift(rng.get()); + rng.set(x); + res + }) + } + + #[cfg(not(feature = "std"))] + fn rng() -> usize { + // A deterministic seed is used in absense of TLS + static RNG: AtomicUsize = AtomicUsize::new(42); + + // Preemption here can cause multiple threads to observe repeated state + let (x, res) = xorshift(RNG.load(Ordering::Relaxed)); + RNG.store(x, Ordering::Relaxed); + res + } + + rng() } diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs index 44a995fc9c..21645b148d 100644 --- a/futures-util/src/lib.rs +++ b/futures-util/src/lib.rs @@ -39,12 +39,10 @@ pub use futures_core::ready; pub use pin_utils::pin_mut; // Not public API. -#[cfg(feature = "std")] #[cfg(feature = "async-await")] #[macro_use] #[doc(hidden)] pub mod async_await; -#[cfg(feature = "std")] #[cfg(feature = "async-await")] #[doc(hidden)] pub use self::async_await::*; diff --git a/futures/src/lib.rs b/futures/src/lib.rs index d4f248257a..bfeebf04f9 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -542,13 +542,11 @@ pub mod never { pub use futures_core::core_reexport; // Not public API. -#[cfg(feature = "std")] #[cfg(feature = "async-await")] #[doc(hidden)] pub use futures_util::async_await; // Not public API. -#[cfg(feature = "std")] #[cfg(feature = "async-await")] #[doc(hidden)] pub mod inner_macro { @@ -558,7 +556,6 @@ pub mod inner_macro { pub use futures_util::select_biased; } -#[cfg(feature = "std")] #[cfg(feature = "async-await")] futures_util::document_join_macro! { #[macro_export] @@ -582,7 +579,6 @@ futures_util::document_join_macro! { } } -#[cfg(feature = "std")] #[cfg(feature = "async-await")] futures_util::document_select_macro! { #[macro_export]