use core::future::Future;
use core::pin::Pin;
use core::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use core::time::Duration;
use std::sync::Arc;
#[cfg(not(target_arch = "wasm32"))]
pub trait MaybeSend: Send {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Send + ?Sized> MaybeSend for T {}
#[cfg(target_arch = "wasm32")]
pub trait MaybeSend {}
#[cfg(target_arch = "wasm32")]
impl<T: ?Sized> MaybeSend for T {}
#[cfg(not(target_arch = "wasm32"))]
pub trait MaybeSync: Sync {}
#[cfg(not(target_arch = "wasm32"))]
impl<T: Sync + ?Sized> MaybeSync for T {}
#[cfg(target_arch = "wasm32")]
pub trait MaybeSync {}
#[cfg(target_arch = "wasm32")]
impl<T: ?Sized> MaybeSync for T {}
#[cfg(not(target_arch = "wasm32"))]
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[cfg(target_arch = "wasm32")]
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
#[cfg(not(target_arch = "wasm32"))]
pub type BoxStream<'a, T> = Pin<Box<dyn futures_core::Stream<Item = T> + Send + 'a>>;
#[cfg(target_arch = "wasm32")]
pub type BoxStream<'a, T> = Pin<Box<dyn futures_core::Stream<Item = T> + 'a>>;
pub trait Clock: MaybeSend + MaybeSync {
fn now_ms(&self) -> i64;
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, Default, Clone, Copy)]
pub struct SystemClock;
#[cfg(not(target_arch = "wasm32"))]
impl Clock for SystemClock {
fn now_ms(&self) -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(since) => i64::try_from(since.as_millis()).unwrap_or(i64::MAX),
Err(before) => i64::try_from(before.duration().as_millis()).map_or(i64::MIN, |ms| -ms),
}
}
}
#[derive(Debug, Default)]
pub struct ManualClock {
now_ms: AtomicI64,
}
impl ManualClock {
#[must_use]
pub const fn new(start_ms: i64) -> Self {
Self {
now_ms: AtomicI64::new(start_ms),
}
}
pub fn set(&self, now_ms: i64) {
self.now_ms.store(now_ms, Ordering::SeqCst);
}
pub fn advance(&self, delta_ms: i64) {
self.now_ms.fetch_add(delta_ms, Ordering::SeqCst);
}
}
impl Clock for ManualClock {
fn now_ms(&self) -> i64 {
self.now_ms.load(Ordering::SeqCst)
}
}
pub trait Spawner: MaybeSend + MaybeSync {
fn spawn(&self, fut: BoxFuture<'static, ()>);
}
pub trait Sleep: MaybeSend + MaybeSync {
fn sleep(&self, duration: Duration) -> BoxFuture<'static, ()>;
}
impl<T: Sleep + ?Sized> Sleep for Arc<T> {
fn sleep(&self, duration: Duration) -> BoxFuture<'static, ()> {
(**self).sleep(duration)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Elapsed;
pub async fn with_timeout<S, F>(sleep: &S, after: Duration, fut: F) -> Result<F::Output, Elapsed>
where
S: Sleep + ?Sized,
F: Future,
{
let fut = core::pin::pin!(fut);
match futures::future::select(fut, sleep.sleep(after)).await {
futures::future::Either::Left((output, _)) => Ok(output),
futures::future::Either::Right(((), _)) => Err(Elapsed),
}
}
#[derive(Debug, Clone, Default)]
pub struct ManualSleep {
fired: Arc<AtomicBool>,
wake: Arc<tokio::sync::Notify>,
requested: Arc<std::sync::Mutex<Vec<Duration>>>,
}
impl ManualSleep {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn elapsed() -> Self {
let sleeper = Self::default();
sleeper.fire();
sleeper
}
#[must_use]
pub fn requested(&self) -> Vec<Duration> {
self.requested
.lock()
.map_or_else(|poisoned| poisoned.into_inner().clone(), |v| v.clone())
}
pub fn fire(&self) {
self.fired.store(true, Ordering::SeqCst);
self.wake.notify_waiters();
}
}
impl Sleep for ManualSleep {
fn sleep(&self, duration: Duration) -> BoxFuture<'static, ()> {
match self.requested.lock() {
Ok(mut requested) => requested.push(duration),
Err(poisoned) => poisoned.into_inner().push(duration),
}
let (fired, wake) = (self.fired.clone(), self.wake.clone());
Box::pin(async move {
loop {
let notified = wake.notified();
if fired.load(Ordering::SeqCst) {
return;
}
notified.await;
}
})
}
}
#[cfg(not(target_arch = "wasm32"))]
#[must_use]
pub fn send_wrap<F: Future + Send>(f: F) -> F {
f
}
#[cfg(target_arch = "wasm32")]
#[must_use]
pub fn send_wrap<F: Future>(f: F) -> send_wrapper::SendWrapper<F> {
send_wrapper::SendWrapper::new(f)
}
#[cfg(all(feature = "connect", not(target_arch = "wasm32")))]
pub(crate) fn send_wrap_stream<S: futures_core::Stream + Send>(s: S) -> S {
s
}
#[cfg(all(feature = "connect", target_arch = "wasm32"))]
pub(crate) fn send_wrap_stream<S: futures_core::Stream>(s: S) -> send_wrapper::SendWrapper<S> {
send_wrapper::SendWrapper::new(s)
}
#[cfg(all(test, not(target_arch = "wasm32")))]
mod tests {
use super::*;
#[test]
fn manual_clock_advances() {
let clock = ManualClock::new(1_000);
assert_eq!(clock.now_ms(), 1_000);
clock.advance(250);
assert_eq!(clock.now_ms(), 1_250);
clock.set(7);
assert_eq!(clock.now_ms(), 7);
assert_eq!(ManualClock::default().now_ms(), 0);
}
#[test]
fn system_clock_is_after_2020() {
assert!(SystemClock.now_ms() > 1_577_836_800_000);
}
#[test]
fn send_wrap_is_identity_on_native() {
let fut = send_wrap(async { 41 + 1 });
assert_eq!(futures_executor::block_on(fut), 42);
}
fn assert_send<T: Send>(_: &T) {}
trait Service {
fn call(&self) -> impl Future<Output = u8> + MaybeSend;
}
struct Seven;
impl Service for Seven {
async fn call(&self) -> u8 {
7
}
}
fn require_send_from_generic<S: Service>(service: &S) -> u8 {
let fut = service.call();
assert_send(&fut);
futures_executor::block_on(fut)
}
#[test]
fn maybe_send_future_is_send_to_generic_callers_on_native() {
assert_eq!(require_send_from_generic(&Seven), 7);
let boxed: BoxFuture<'static, u8> = Box::pin(Seven.call());
assert_send(&boxed);
}
#[test]
fn with_timeout_returns_a_ready_future_and_times_out_a_pending_one() {
let never = ManualSleep::new();
let ready = with_timeout(&never, Duration::from_secs(5), async { 9 });
assert_eq!(futures_executor::block_on(ready), Ok(9));
let fired = ManualSleep::elapsed();
let stuck = with_timeout(
&fired,
Duration::from_secs(5),
core::future::pending::<u8>(),
);
assert_eq!(futures_executor::block_on(stuck), Err(Elapsed));
let both = with_timeout(&fired, Duration::from_secs(5), async { 3 });
assert_eq!(futures_executor::block_on(both), Ok(3));
}
#[test]
fn manual_sleep_wakes_a_pending_sleep_when_fired() {
let sleeper = ManualSleep::new();
let timer = sleeper.sleep(Duration::from_secs(1));
let woken = std::sync::Arc::new(core::sync::atomic::AtomicBool::new(false));
let flag = woken.clone();
let handle = std::thread::spawn(move || {
futures_executor::block_on(timer);
flag.store(true, Ordering::SeqCst);
});
std::thread::sleep(Duration::from_millis(50));
assert!(!woken.load(Ordering::SeqCst));
sleeper.fire();
handle.join().unwrap();
assert!(woken.load(Ordering::SeqCst));
}
#[test]
fn spawner_accepts_boxed_futures() {
struct Inline;
impl Spawner for Inline {
fn spawn(&self, fut: BoxFuture<'static, ()>) {
futures_executor::block_on(fut);
}
}
let ran = std::sync::Arc::new(core::sync::atomic::AtomicBool::new(false));
let flag = ran.clone();
Inline.spawn(Box::pin(async move { flag.store(true, Ordering::SeqCst) }));
assert!(ran.load(Ordering::SeqCst));
}
}