Skip to content

Commit 7212aac

Browse files
committed
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.
1 parent f5c6439 commit 7212aac

File tree

4 files changed

+74
-23
lines changed

4 files changed

+74
-23
lines changed

futures-util/src/async_await/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@ mod select_mod;
3131
#[cfg(feature = "async-await-macro")]
3232
pub use self::select_mod::*;
3333

34-
#[cfg(feature = "std")]
3534
#[cfg(feature = "async-await-macro")]
3635
mod random;
37-
#[cfg(feature = "std")]
3836
#[cfg(feature = "async-await-macro")]
3937
pub use self::random::*;
4038

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
use core::sync::atomic::{AtomicUsize, Ordering};
2+
#[cfg(feature = "std")]
13
use std::{
24
cell::Cell,
35
collections::hash_map::DefaultHasher,
46
hash::Hasher,
5-
num::Wrapping,
6-
sync::atomic::{AtomicUsize, Ordering},
77
};
88

99
// Based on [Fisher–Yates shuffle].
@@ -18,18 +18,15 @@ pub fn shuffle<T>(slice: &mut [T]) {
1818

1919
/// Return a value from `0..n`.
2020
fn gen_index(n: usize) -> usize {
21-
(random() % n as u64) as usize
21+
random() % n
2222
}
2323

24-
/// Pseudorandom number generator based on [xorshift*].
24+
/// Pseudorandom number generator based on [xorshift].
2525
///
26-
/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
27-
fn random() -> u64 {
28-
thread_local! {
29-
static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(prng_seed()));
30-
}
31-
32-
fn prng_seed() -> u64 {
26+
/// [xorshift]: https://en.wikipedia.org/wiki/Xorshift
27+
fn random() -> usize {
28+
#[cfg(feature = "std")]
29+
fn prng_seed() -> usize {
3330
static COUNTER: AtomicUsize = AtomicUsize::new(0);
3431

3532
// Any non-zero seed will do
@@ -39,16 +36,75 @@ fn random() -> u64 {
3936
hasher.write_usize(COUNTER.fetch_add(1, Ordering::Relaxed));
4037
seed = hasher.finish();
4138
}
42-
seed
39+
seed as usize
4340
}
4441

45-
RNG.with(|rng| {
46-
let mut x = rng.get();
47-
debug_assert_ne!(x.0, 0);
42+
#[cfg(not(feature = "std"))]
43+
const fn prng_seed() -> usize {
44+
// A deterministic seed is used in absense of TLS
45+
42
46+
}
47+
48+
/// [xorshift*] is used on 64bit platforms.
49+
///
50+
/// [xorshift*]: https://en.wikipedia.org/wiki/Xorshift#xorshift*
51+
#[cfg(target_pointer_width = "64")]
52+
fn xorshift(mut x: usize) -> (usize, usize) {
53+
debug_assert_ne!(x, 0);
4854
x ^= x >> 12;
4955
x ^= x << 25;
5056
x ^= x >> 27;
51-
rng.set(x);
52-
x.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
53-
})
57+
(
58+
x,
59+
x.wrapping_mul(0x2545_f491_4f6c_dd1d),
60+
)
61+
}
62+
63+
/// [xorshift32] is used on 32bit platforms.
64+
///
65+
/// [xorshift32]: https://en.wikipedia.org/wiki/Xorshift
66+
#[cfg(target_pointer_width = "32")]
67+
fn xorshift(mut x: usize) -> (usize, usize) {
68+
debug_assert_ne!(x, 0);
69+
x ^= x << 13;
70+
x ^= x >> 17;
71+
x ^= x << 5;
72+
(x, x)
73+
}
74+
75+
/// A non-standard xorshift variant is used on 16bit platforms.
76+
#[cfg(target_pointer_width = "16")]
77+
fn xorshift(mut x: usize) -> (usize, usize) {
78+
// Constants chosen from: http://b2d-f9r.blogspot.com/2010/08/16-bit-xorshift-rng.html
79+
debug_assert_ne!(x, 0);
80+
x ^= x << 4;
81+
x ^= x >> 3;
82+
x ^= x << 7;
83+
(x, x)
84+
}
85+
86+
#[cfg(feature = "std")]
87+
fn rng() -> usize {
88+
thread_local! {
89+
static RNG: Cell<usize> = Cell::new(prng_seed());
90+
}
91+
92+
RNG.with(|rng| {
93+
let (x, res) = xorshift(rng.get());
94+
rng.set(x);
95+
res
96+
})
97+
}
98+
99+
#[cfg(not(feature = "std"))]
100+
fn rng() -> usize {
101+
static RNG: AtomicUsize = AtomicUsize::new(prng_seed());
102+
103+
// Preemption here can cause multiple threads to observe repeated state
104+
let (x, res) = xorshift(RNG.load(Ordering::Relaxed));
105+
RNG.store(x, Ordering::Relaxed);
106+
res
107+
}
108+
109+
rng()
54110
}

futures-util/src/async_await/select_mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,6 @@ macro_rules! document_select_macro {
306306
}
307307

308308
document_select_macro! {
309-
#[cfg(feature = "std")]
310309
#[proc_macro_hack(support_nested)]
311310
pub use futures_macro::select;
312311

futures/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,6 @@ pub use futures_util::async_await;
552552
pub mod inner_macro {
553553
pub use futures_util::join;
554554
pub use futures_util::try_join;
555-
#[cfg(feature = "std")]
556555
pub use futures_util::select;
557556
pub use futures_util::select_biased;
558557
}
@@ -582,7 +581,6 @@ futures_util::document_join_macro! {
582581

583582
#[cfg(feature = "async-await")]
584583
futures_util::document_select_macro! {
585-
#[cfg(feature = "std")]
586584
#[macro_export]
587585
macro_rules! select { // replace `::futures_util` with `::futures` as the crate path
588586
($($tokens:tt)*) => {

0 commit comments

Comments
 (0)