From 1e0e7d83eae4e02a11d5d1525e21c9e68844b916 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Dec 2024 15:10:51 +0900 Subject: [PATCH 1/7] feat: `select!` macro for no std environments Currently, `select!` macro works on only 64 bit architectures. Signed-off-by: Yuuki Takano --- futures-util/src/async_await/mod.rs | 2 -- futures-util/src/async_await/random.rs | 34 +++++++++++++++++----- futures-util/src/async_await/select_mod.rs | 2 -- futures/src/lib.rs | 1 - 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/futures-util/src/async_await/mod.rs b/futures-util/src/async_await/mod.rs index 09152f94c..f2cd9fc62 100644 --- a/futures-util/src/async_await/mod.rs +++ b/futures-util/src/async_await/mod.rs @@ -34,10 +34,8 @@ mod stream_select_mod; #[cfg(feature = "async-await-macro")] pub use self::stream_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 2ac2f78a8..cea9c3cc3 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -1,11 +1,3 @@ -use std::{ - cell::Cell, - collections::hash_map::DefaultHasher, - hash::Hasher, - num::Wrapping, - sync::atomic::{AtomicUsize, Ordering}, -}; - // Based on [Fisher–Yates shuffle]. // // [Fisher–Yates shuffle]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle @@ -24,7 +16,16 @@ fn gen_index(n: usize) -> usize { /// Pseudorandom number generator based on [xorshift*]. /// /// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift* +#[cfg(feature = "std")] fn random() -> u64 { + use std::{ + cell::Cell, + collections::hash_map::DefaultHasher, + hash::Hasher, + num::Wrapping, + sync::atomic::{AtomicUsize, Ordering}, + }; + std::thread_local! { static RNG: Cell> = Cell::new(Wrapping(prng_seed())); } @@ -52,3 +53,20 @@ fn random() -> u64 { x.0.wrapping_mul(0x2545_f491_4f6c_dd1d) }) } + +#[cfg(not(feature = "std"))] +fn random() -> u64 { + use core::sync::atomic::{AtomicU64, Ordering}; + + static RNG: AtomicU64 = AtomicU64::new(1); + + let mut x = RNG.load(Ordering::Relaxed); + + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + let result = x.wrapping_mul(0x2545_f491_4f6c_dd1d) as u64; + RNG.store(result, Ordering::Relaxed); + + result +} diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs index 638a92eb1..de801e1ad 100644 --- a/futures-util/src/async_await/select_mod.rs +++ b/futures-util/src/async_await/select_mod.rs @@ -305,7 +305,6 @@ macro_rules! document_select_macro { }; } -#[cfg(feature = "std")] #[doc(hidden)] pub use futures_macro::select_internal; @@ -313,7 +312,6 @@ pub use futures_macro::select_internal; pub use futures_macro::select_biased_internal; document_select_macro! { - #[cfg(feature = "std")] #[macro_export] macro_rules! select { ($($tokens:tt)*) => {{ diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 839a0a1f0..fececa498 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -120,7 +120,6 @@ pub use futures_util::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteEx // Macro reexports pub use futures_core::ready; // Readiness propagation pub use futures_util::pin_mut; -#[cfg(feature = "std")] #[cfg(feature = "async-await")] pub use futures_util::select; #[cfg(feature = "async-await")] From c4d0535347f0bb4f49e76efa56a164604aae4f2b Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Dec 2024 15:21:07 +0900 Subject: [PATCH 2/7] fix: use AtomicUsize for random() Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index cea9c3cc3..ef6b480e4 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -56,15 +56,22 @@ fn random() -> u64 { #[cfg(not(feature = "std"))] fn random() -> u64 { - use core::sync::atomic::{AtomicU64, Ordering}; + use core::sync::atomic::{AtomicUsize, Ordering}; - static RNG: AtomicU64 = AtomicU64::new(1); + static RNG: AtomicUsize = AtomicUsize::new(1); let mut x = RNG.load(Ordering::Relaxed); - x ^= x >> 12; - x ^= x << 25; - x ^= x >> 27; + if core::mem::size_of::() == 4 { + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + } else if core::mem::size_of::() == 8 { + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + } + let result = x.wrapping_mul(0x2545_f491_4f6c_dd1d) as u64; RNG.store(result, Ordering::Relaxed); From 587004485115aba402b51619cdf07ef3f955aab4 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Dec 2024 15:37:35 +0900 Subject: [PATCH 3/7] fix a bug Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index ef6b480e4..772995f2a 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -73,7 +73,7 @@ fn random() -> u64 { } let result = x.wrapping_mul(0x2545_f491_4f6c_dd1d) as u64; - RNG.store(result, Ordering::Relaxed); + RNG.store(result as usize, Ordering::Relaxed); result } From 4f776f91af815b16c1b4cbf013185e1c6fe04065 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Wed, 25 Dec 2024 15:43:26 +0900 Subject: [PATCH 4/7] fix Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 772995f2a..df66d9641 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -70,10 +70,9 @@ fn random() -> u64 { x ^= x >> 12; x ^= x << 25; x ^= x >> 27; + x = x.wrapping_mul(0x2545_f491_4f6c_dd1d); } - let result = x.wrapping_mul(0x2545_f491_4f6c_dd1d) as u64; - RNG.store(result as usize, Ordering::Relaxed); - - result + RNG.store(x, Ordering::Relaxed); + x as u64 } From 9e7d2f759d8f120ce7ef1cdb1870141dc75baef6 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 14 Jan 2025 14:47:04 +0900 Subject: [PATCH 5/7] fix: define `xorshift64star` and `xorshift32` functions Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 55 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index df66d9641..8f9dd72f2 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -1,3 +1,5 @@ +use core::num::Wrapping; + // Based on [Fisher–Yates shuffle]. // // [Fisher–Yates shuffle]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle @@ -22,7 +24,6 @@ fn random() -> u64 { cell::Cell, collections::hash_map::DefaultHasher, hash::Hasher, - num::Wrapping, sync::atomic::{AtomicUsize, Ordering}, }; @@ -44,35 +45,51 @@ fn random() -> u64 { } RNG.with(|rng| { - let mut x = rng.get(); - debug_assert_ne!(x.0, 0); - x ^= x >> 12; - x ^= x << 25; - x ^= x >> 27; - rng.set(x); - x.0.wrapping_mul(0x2545_f491_4f6c_dd1d) + let x = rng.get(); + let (next, result) = xorshift64star(x); + rng.set(next); + result }) } #[cfg(not(feature = "std"))] -fn random() -> u64 { +fn random_nostd() -> u64 { use core::sync::atomic::{AtomicUsize, Ordering}; static RNG: AtomicUsize = AtomicUsize::new(1); - let mut x = RNG.load(Ordering::Relaxed); + let x = RNG.load(Ordering::Relaxed); if core::mem::size_of::() == 4 { - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; + let next = xorshift32(x as u32); + RNG.store(next as usize, Ordering::Relaxed); + next as u64 } else if core::mem::size_of::() == 8 { - x ^= x >> 12; - x ^= x << 25; - x ^= x >> 27; - x = x.wrapping_mul(0x2545_f491_4f6c_dd1d); + let (next, result) = xorshift64star(Wrapping(x as u64)); + RNG.store(next.0 as usize, Ordering::Relaxed); + result + } else { + panic!("random() function is not supported on this platform"); } +} - RNG.store(x, Ordering::Relaxed); - x as u64 +/// Xorshift64* algorithm. +/// Returns the next state and the random number; `(next_state, random_number)`. +#[inline] +fn xorshift64star(mut x: Wrapping) -> (Wrapping, u64) { + debug_assert_ne!(x.0, 0); + x ^= x >> 12; + x ^= x << 25; + x ^= x >> 27; + (x, x.0.wrapping_mul(0x2545_f491_4f6c_dd1d)) +} + +/// Xorshift32 algorithm. +#[cfg(not(feature = "std"))] +#[inline] +fn xorshift32(mut x: u32) -> u32 { + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + x } From b71bf7e12513ebfcff561f49b12f7455826132f3 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 14 Jan 2025 14:50:11 +0900 Subject: [PATCH 6/7] fix: typo s/random_nostd/random/ Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 8f9dd72f2..259a68052 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -53,7 +53,7 @@ fn random() -> u64 { } #[cfg(not(feature = "std"))] -fn random_nostd() -> u64 { +fn random() -> u64 { use core::sync::atomic::{AtomicUsize, Ordering}; static RNG: AtomicUsize = AtomicUsize::new(1); From d06f2266caebd72c4522703a856cb36ece82c311 Mon Sep 17 00:00:00 2001 From: Yuuki Takano Date: Tue, 14 Jan 2025 14:51:47 +0900 Subject: [PATCH 7/7] chore: add debug_assert_ne to check x is not zero Signed-off-by: Yuuki Takano --- futures-util/src/async_await/random.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/futures-util/src/async_await/random.rs b/futures-util/src/async_await/random.rs index 259a68052..e8d53e604 100644 --- a/futures-util/src/async_await/random.rs +++ b/futures-util/src/async_await/random.rs @@ -88,6 +88,7 @@ fn xorshift64star(mut x: Wrapping) -> (Wrapping, u64) { #[cfg(not(feature = "std"))] #[inline] fn xorshift32(mut x: u32) -> u32 { + debug_assert_ne!(x, 0); x ^= x << 13; x ^= x >> 17; x ^= x << 5;