Skip to content

Commit eb78d89

Browse files
authored
Use getrandom for seeding the RNG on WASM targets (#60)
1 parent 46527e0 commit eb78d89

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ exclude = ["/.*"]
1717
[features]
1818
default = ["std"]
1919
alloc = []
20-
std = ["alloc", "instant"]
20+
std = ["alloc"]
21+
js = ["std", "getrandom"]
2122

22-
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
23-
instant = { version = "0.1", optional = true }
23+
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies]
24+
getrandom = { version = "0.2", features = ["js"], optional = true }
2425

25-
[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies]
26-
instant = { version = "0.1", features = ["wasm-bindgen"] }
26+
[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies]
2727
wasm-bindgen-test = "0.3"
2828
getrandom = { version = "0.2", features = ["js"] }
2929

src/global_rng.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ use crate::Rng;
55
use std::cell::Cell;
66
use std::ops::RangeBounds;
77

8-
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
9-
use instant::Instant;
10-
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
11-
use std::time::Instant;
8+
// Chosen by fair roll of the dice.
9+
const DEFAULT_RNG_SEED: u64 = 0xef6f79ed30ba75a;
1210

1311
impl Default for Rng {
1412
/// Initialize the `Rng` from the system's random number generator.
@@ -29,17 +27,7 @@ impl Rng {
2927
}
3028

3129
thread_local! {
32-
static RNG: Cell<Rng> = Cell::new(Rng({
33-
use std::collections::hash_map::DefaultHasher;
34-
use std::hash::{Hash, Hasher};
35-
use std::thread;
36-
37-
let mut hasher = DefaultHasher::new();
38-
Instant::now().hash(&mut hasher);
39-
thread::current().id().hash(&mut hasher);
40-
let hash = hasher.finish();
41-
(hash << 1) | 1
42-
}));
30+
static RNG: Cell<Rng> = Cell::new(Rng(random_seed().unwrap_or(DEFAULT_RNG_SEED)));
4331
}
4432

4533
/// Run an operation with the current thread-local generator.
@@ -190,3 +178,41 @@ pub fn f64() -> f64 {
190178
pub fn choose_multiple<T: Iterator>(source: T, amount: usize) -> Vec<T::Item> {
191179
with_rng(|rng| rng.choose_multiple(source, amount))
192180
}
181+
182+
#[cfg(not(all(
183+
any(target_arch = "wasm32", target_arch = "wasm64"),
184+
target_os = "unknown"
185+
)))]
186+
fn random_seed() -> Option<u64> {
187+
use std::collections::hash_map::DefaultHasher;
188+
use std::hash::{Hash, Hasher};
189+
use std::thread;
190+
use std::time::Instant;
191+
192+
let mut hasher = DefaultHasher::new();
193+
Instant::now().hash(&mut hasher);
194+
thread::current().id().hash(&mut hasher);
195+
let hash = hasher.finish();
196+
Some((hash << 1) | 1)
197+
}
198+
199+
#[cfg(all(
200+
any(target_arch = "wasm32", target_arch = "wasm64"),
201+
target_os = "unknown",
202+
feature = "js"
203+
))]
204+
fn random_seed() -> Option<u64> {
205+
// TODO(notgull): Failures should be logged somewhere.
206+
let mut seed = [0u8; 8];
207+
getrandom::getrandom(&mut seed).ok()?;
208+
Some(u64::from_ne_bytes(seed))
209+
}
210+
211+
#[cfg(all(
212+
any(target_arch = "wasm32", target_arch = "wasm64"),
213+
target_os = "unknown",
214+
not(feature = "js")
215+
))]
216+
fn random_seed() -> Option<u64> {
217+
None
218+
}

src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@
7878
//! - `std` (enabled by default): Enables the `std` library. This is required for the global
7979
//! generator and global entropy. Without this feature, [`Rng`] can only be instantiated using
8080
//! the [`with_seed`](Rng::with_seed) method.
81+
//! - `js`: Assumes that WebAssembly targets are being run in a JavaScript environment. See the
82+
//! [WebAssembly Notes](#webassembly-notes) section for more information.
83+
//!
84+
//! # WebAssembly Notes
85+
//!
86+
//! For non-WASI WASM targets, there is additional sublety to consider when utilizing the global RNG.
87+
//! By default, `std` targets will use entropy sources in the standard library to seed the global RNG.
88+
//! However, these sources are not available by default on WASM targets outside of WASI.
89+
//!
90+
//! If the `js` feature is enabled, this crate will assume that it is running in a JavaScript
91+
//! environment. At this point, the [`getrandom`] crate will be used in order to access the available
92+
//! entropy sources and seed the global RNG. If the `js` feature is not enabled, the global RNG will
93+
//! use a predefined seed.
94+
//!
95+
//! [`getrandom`]: https://crates.io/crates/getrandom
8196
8297
#![cfg_attr(not(feature = "std"), no_std)]
8398
#![cfg_attr(docsrs, feature(doc_cfg))]

0 commit comments

Comments
 (0)