use std::cell::Cell;
use std::fmt::Debug;
use std::future::Future;
use std::io;
use std::time::Duration;
use rand::SeedableRng;
use rand::rngs::SmallRng;
use crate::AsyncRuntime;
use crate::OptionalSend;
const GOLDEN_RATIO: u64 = 0x9e3779b97f4a7c15;
const SQRT2: u64 = 0x6a09e667f3bcc908;
task_local! {
static DETSIM_SEED: Cell<u64>;
}
fn splitmix64(seed: u64, gamma: u64) -> u64 {
let z = seed.wrapping_add(gamma);
let z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
let z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
z ^ (z >> 31)
}
fn derive_spawn_seed(seed: u64) -> u64 {
splitmix64(seed, GOLDEN_RATIO)
}
fn advance_seed(seed: u64) -> u64 {
splitmix64(seed, SQRT2)
}
fn take_and_advance_seed() -> u64 {
DETSIM_SEED.with(|c| {
let seed = c.get();
c.set(advance_seed(seed));
seed
})
}
pub struct DeterministicRng<RT>
where RT: AsyncRuntime
{
inner: RT,
seed: u64,
}
impl<RT> DeterministicRng<RT>
where RT: AsyncRuntime
{
pub fn set_seed(&mut self, seed: u64) {
self.seed = seed;
}
pub fn scope<F>(seed: u64, f: F) -> crate::task_local::TaskLocalFuture<Cell<u64>, F>
where F: Future {
DETSIM_SEED.scope(Cell::new(seed), f)
}
}
impl<RT> Debug for DeterministicRng<RT>
where RT: AsyncRuntime
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DeterministicRng").field("inner", &self.inner).finish()
}
}
impl<RT> AsyncRuntime for DeterministicRng<RT>
where RT: AsyncRuntime
{
type JoinError = RT::JoinError;
type JoinHandle<T: OptionalSend + 'static> = RT::JoinHandle<T>;
type Sleep = RT::Sleep;
type Instant = RT::Instant;
type TimeoutError = RT::TimeoutError;
type Timeout<R, T: Future<Output = R> + OptionalSend> = RT::Timeout<R, T>;
type ThreadLocalRng = SmallRng;
#[inline]
fn spawn<T>(future: T) -> Self::JoinHandle<T::Output>
where
T: Future + OptionalSend + 'static,
T::Output: OptionalSend + 'static,
{
let child_seed = derive_spawn_seed(take_and_advance_seed());
RT::spawn(DETSIM_SEED.scope(Cell::new(child_seed), future))
}
#[inline]
fn sleep(duration: Duration) -> Self::Sleep {
RT::sleep(duration)
}
#[inline]
fn sleep_until(deadline: Self::Instant) -> Self::Sleep {
RT::sleep_until(deadline)
}
#[inline]
fn timeout<R, F>(duration: Duration, future: F) -> Self::Timeout<R, F>
where F: Future<Output = R> + OptionalSend {
RT::timeout(duration, future)
}
#[inline]
fn timeout_at<R, F>(deadline: Self::Instant, future: F) -> Self::Timeout<R, F>
where F: Future<Output = R> + OptionalSend {
RT::timeout_at(deadline, future)
}
#[inline]
fn is_panic(join_error: &Self::JoinError) -> bool {
RT::is_panic(join_error)
}
#[inline]
fn thread_rng() -> Self::ThreadLocalRng {
SmallRng::seed_from_u64(take_and_advance_seed())
}
type Mpsc = RT::Mpsc;
type Watch = RT::Watch;
type Oneshot = RT::Oneshot;
type Mutex<T: OptionalSend + 'static> = RT::Mutex<T>;
fn new(threads: usize) -> Self {
DeterministicRng {
inner: RT::new(threads),
seed: 0,
}
}
fn block_on<F, T>(&mut self, future: F) -> T
where
F: Future<Output = T>,
T: OptionalSend,
{
DETSIM_SEED.sync_scope(Cell::new(self.seed), || {
let result = self.inner.block_on(future);
self.seed = DETSIM_SEED.with(|c| c.get());
result
})
}
fn spawn_blocking<F, T>(f: F) -> impl Future<Output = Result<T, io::Error>> + Send
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
RT::spawn_blocking(f)
}
}