use alloc::boxed::Box;
use core::future::Future;
use core::pin::Pin;
use core::time::Duration;
use async_trait::async_trait;
use crate::marker::{MaybeSend, MaybeSync};
#[cfg(any(feature = "std", all(feature = "wasi", target_os = "wasi")))]
fn monotonic_millis() -> u64 {
use std::sync::OnceLock;
use std::time::Instant;
static ORIGIN: OnceLock<Instant> = OnceLock::new();
ORIGIN.get_or_init(Instant::now).elapsed().as_millis() as u64
}
#[cfg(any(feature = "std", all(feature = "wasi", target_os = "wasi")))]
fn system_unix_millis() -> i64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|elapsed| elapsed.as_millis() as i64)
.unwrap_or_default()
}
#[cfg(not(target_family = "wasm"))]
pub type BoxFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
#[cfg(target_family = "wasm")]
pub type BoxFuture = Pin<Box<dyn Future<Output = ()>>>;
pub trait TaskHandle: core::fmt::Debug + MaybeSend + MaybeSync {
fn abort(&self);
}
#[cfg_attr(not(target_family = "wasm"), async_trait)]
#[cfg_attr(target_family = "wasm", async_trait(?Send))]
pub trait Runtime: core::fmt::Debug + MaybeSend + MaybeSync {
async fn sleep(&self, duration: Duration);
fn spawn(&self, future: BoxFuture) -> Box<dyn TaskHandle>;
fn now_millis(&self) -> u64;
fn unix_millis(&self) -> i64;
}
#[cfg(feature = "std")]
#[derive(Clone, Copy, Debug, Default)]
pub struct TokioRuntime;
#[cfg(feature = "std")]
#[derive(Debug)]
struct TokioTask(tokio::task::JoinHandle<()>);
#[cfg(feature = "std")]
impl TaskHandle for TokioTask {
fn abort(&self) {
self.0.abort();
}
}
#[cfg(feature = "std")]
#[async_trait]
impl Runtime for TokioRuntime {
async fn sleep(&self, duration: Duration) {
tokio::time::sleep(duration).await;
}
fn spawn(&self, future: BoxFuture) -> Box<dyn TaskHandle> {
Box::new(TokioTask(tokio::spawn(future)))
}
fn now_millis(&self) -> u64 {
monotonic_millis()
}
fn unix_millis(&self) -> i64 {
system_unix_millis()
}
}
#[cfg(all(feature = "wasm", target_family = "wasm", target_os = "unknown"))]
#[derive(Clone, Copy, Debug, Default)]
pub struct WasmRuntime;
#[cfg(all(feature = "wasm", target_family = "wasm", target_os = "unknown"))]
#[derive(Debug)]
struct WasmTask(futures::future::AbortHandle);
#[cfg(all(feature = "wasm", target_family = "wasm", target_os = "unknown"))]
impl TaskHandle for WasmTask {
fn abort(&self) {
self.0.abort();
}
}
#[cfg(all(feature = "wasm", target_family = "wasm", target_os = "unknown"))]
#[async_trait(?Send)]
impl Runtime for WasmRuntime {
async fn sleep(&self, duration: Duration) {
let millis = u32::try_from(duration.as_millis()).unwrap_or(u32::MAX);
gloo_timers::future::TimeoutFuture::new(millis).await;
}
fn spawn(&self, future: BoxFuture) -> Box<dyn TaskHandle> {
let (handle, registration) = futures::future::AbortHandle::new_pair();
let task = futures::future::Abortable::new(future, registration);
wasm_bindgen_futures::spawn_local(async move {
let _ = task.await;
});
Box::new(WasmTask(handle))
}
fn now_millis(&self) -> u64 {
web_sys::window()
.and_then(|window| window.performance())
.map(|performance| performance.now())
.unwrap_or_else(js_sys::Date::now) as u64
}
fn unix_millis(&self) -> i64 {
js_sys::Date::now() as i64
}
}
#[cfg(all(feature = "wasi", target_os = "wasi"))]
#[derive(Clone, Copy, Debug, Default)]
pub struct WasiRuntime;
#[cfg(all(feature = "wasi", target_os = "wasi"))]
#[derive(Debug)]
struct WasiTask;
#[cfg(all(feature = "wasi", target_os = "wasi"))]
impl TaskHandle for WasiTask {
fn abort(&self) {}
}
#[cfg(all(feature = "wasi", target_os = "wasi"))]
#[async_trait(?Send)]
impl Runtime for WasiRuntime {
async fn sleep(&self, duration: Duration) {
let millis = u64::try_from(duration.as_millis()).unwrap_or(u64::MAX);
wstd::task::sleep(wstd::time::Duration::from_millis(millis)).await;
}
fn spawn(&self, _future: BoxFuture) -> Box<dyn TaskHandle> {
Box::new(WasiTask)
}
fn now_millis(&self) -> u64 {
monotonic_millis()
}
fn unix_millis(&self) -> i64 {
system_unix_millis()
}
}