From 9d8f56e6e92f750c419fe0e9db30f68139d1aac4 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Sat, 28 Sep 2019 06:26:37 -0700 Subject: [PATCH 1/3] Allow async-await macros to be used without std `select!()` is the only macro that requires std due to implementation details, so allow everything else through. --- futures-util/src/async_await/mod.rs | 2 ++ futures-util/src/async_await/select_mod.rs | 1 + futures-util/src/lib.rs | 2 -- futures/src/lib.rs | 6 ++---- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 2831fd62aa..69cae13bed 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -31,8 +31,10 @@ mod select_mod; #[cfg(feature = "async-await-macro")] pub use self::select_mod::*; +#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] mod random; +#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] pub use self::random::*; diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index cdbffba808..628c68379f 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -306,6 +306,7 @@ macro_rules! document_select_macro { } document_select_macro! { + #[cfg(feature = "std")] #[proc_macro_hack(support_nested)] pub use futures_macro::select; 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..8ce578ba9a 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -542,23 +542,21 @@ 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 { pub use futures_util::join; pub use futures_util::try_join; + #[cfg(feature = "std")] pub use futures_util::select; pub use futures_util::select_biased; } -#[cfg(feature = "std")] #[cfg(feature = "async-await")] futures_util::document_join_macro! { #[macro_export] @@ -582,9 +580,9 @@ futures_util::document_join_macro! { } } -#[cfg(feature = "std")] #[cfg(feature = "async-await")] futures_util::document_select_macro! { + #[cfg(feature = "std")] #[macro_export] macro_rules! select { // replace `::futures_util` with `::futures` as the crate path ($($tokens:tt)*) => { From b749779434685951752e7c390951385dbda08b2e Mon Sep 17 00:00:00 2001 From: arcnmx Date: Sat, 28 Sep 2019 08:05:25 -0700 Subject: [PATCH 2/3] Support select! for no-std Targets without libstd (and thus thread-local storage) now use global xorshift state for the select order, which has been weakened for <64bit targets. --- futures-util/src/async_await/mod.rs | 2 - futures-util/src/async_await/random.rs | 92 +++++++++++++++++----- futures-util/src/async_await/select_mod.rs | 1 - futures/src/lib.rs | 2 - 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 69cae13bed..2831fd62aa 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -31,10 +31,8 @@ mod select_mod; #[cfg(feature = "async-await-macro")] pub use self::select_mod::*; -#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] mod random; -#[cfg(feature = "std")] #[cfg(feature = "async-await-macro")] pub use self::random::*; diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 4f8c7254b4..0b3b151c08 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -1,9 +1,9 @@ +use core::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "std")] use std::{ cell::Cell, collections::hash_map::DefaultHasher, hash::Hasher, - num::Wrapping, - sync::atomic::{AtomicUsize, Ordering}, }; // Based on [Fisher–Yates shuffle]. @@ -18,18 +18,15 @@ 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 @@ -39,16 +36,75 @@ fn random() -> u64 { hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed)); seed = hasher.finish(); } - seed + seed as usize } - RNG.with(|rng| { - let mut x = rng.get(); - debug_assert_ne!(x.0, 0); + #[cfg(not(feature = "std"))] + const fn prng_seed() -> usize { + // A deterministic seed is used in absense of TLS + 42 + } + + /// [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 { + static RNG: AtomicUsize = AtomicUsize::new(prng_seed()); + + // 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/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 628c68379f..cdbffba808 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -306,7 +306,6 @@ macro_rules! document_select_macro { } document_select_macro! { - #[cfg(feature = "std")] #[proc_macro_hack(support_nested)] pub use futures_macro::select; diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 8ce578ba9a..bfeebf04f9 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -552,7 +552,6 @@ pub use futures_util::async_await; pub mod inner_macro { pub use futures_util::join; pub use futures_util::try_join; - #[cfg(feature = "std")] pub use futures_util::select; pub use futures_util::select_biased; } @@ -582,7 +581,6 @@ futures_util::document_join_macro! { #[cfg(feature = "async-await")] futures_util::document_select_macro! { - #[cfg(feature = "std")] #[macro_export] macro_rules! select { // replace `::futures_util` with `::futures` as the crate path ($($tokens:tt)*) => { From 03125d75979c06ab0b507f7ff16829748b714ba8 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Tue, 10 Dec 2019 10:58:18 -0800 Subject: [PATCH 3/3] Simplify select seed generation. DefaultHasher is deterministic, so there's little point in using it. --- futures-util/src/async_await/random.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 0b3b151c08..0378fe9fc6 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -1,10 +1,6 @@ use core::sync::atomic::{AtomicUsize, Ordering}; #[cfg(feature = "std")] -use std::{ - cell::Cell, - collections::hash_map::DefaultHasher, - hash::Hasher, -}; +use std::cell::Cell; // Based on [Fisher–Yates shuffle]. // @@ -32,17 +28,9 @@ fn random() -> usize { // 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 as usize - } - - #[cfg(not(feature = "std"))] - const fn prng_seed() -> usize { - // A deterministic seed is used in absense of TLS - 42 + seed } /// [xorshift*] is used on 64bit platforms. @@ -98,7 +86,8 @@ fn random() -> usize { #[cfg(not(feature = "std"))] fn rng() -> usize { - static RNG: AtomicUsize = AtomicUsize::new(prng_seed()); + // 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));