Skip to content

Commit 926b025

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 541a2ae commit 926b025

File tree

2 files changed

+74
-22
lines changed

2 files changed

+74
-22
lines changed

futures-util/src/async_await/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,13 @@ mod join_mod;
2626
pub use self::join_mod::*;
2727

2828
// Primary export is a macro
29-
#[cfg(feature = "std")]
3029
#[cfg(feature = "select-macro")]
3130
mod select_mod;
32-
#[cfg(feature = "std")]
3331
#[cfg(feature = "select-macro")]
3432
pub use self::select_mod::*;
3533

36-
#[cfg(feature = "std")]
3734
#[cfg(feature = "select-macro")]
3835
mod random;
39-
#[cfg(feature = "std")]
4036
#[cfg(feature = "select-macro")]
4137
pub use self::random::*;
4238

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
}

0 commit comments

Comments
 (0)