#[cfg(not(target_arch = "wasm32"))]
pub use native::*;
#[cfg(target_arch = "wasm32")]
pub use wasm::*;
#[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"))]
mod native {
pub use tokio::task::{spawn, JoinError, JoinHandle};
pub use tokio::time::{error::Elapsed, sleep, timeout, Instant};
}
#[cfg(target_arch = "wasm32")]
mod wasm {
use futures::future::{AbortHandle, Abortable};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::sync::oneshot;
pub use web_time::Instant;
#[derive(Debug)]
pub struct JoinError {
aborted: bool,
}
impl JoinError {
pub fn is_cancelled(&self) -> bool {
self.aborted
}
}
impl fmt::Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.aborted {
"task was cancelled"
} else {
"task failed"
})
}
}
impl std::error::Error for JoinError {}
pub struct JoinHandle<T> {
rx: oneshot::Receiver<T>,
abort: AbortHandle,
finished: Arc<AtomicBool>,
}
impl<T> JoinHandle<T> {
pub fn abort(&self) {
self.abort.abort();
}
pub fn is_finished(&self) -> bool {
self.finished.load(Ordering::SeqCst)
}
}
impl<T> Future for JoinHandle<T> {
type Output = Result<T, JoinError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let aborted = self.abort.is_aborted();
Pin::new(&mut self.rx)
.poll(cx)
.map(|r| r.map_err(|_| JoinError { aborted }))
}
}
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
F::Output: 'static,
{
let (tx, rx) = oneshot::channel();
let (abort, registration) = AbortHandle::new_pair();
let finished = Arc::new(AtomicBool::new(false));
let done = finished.clone();
wasm_bindgen_futures::spawn_local(async move {
if let Ok(value) = Abortable::new(future, registration).await {
let _ = tx.send(value);
}
done.store(true, Ordering::SeqCst);
});
JoinHandle {
rx,
abort,
finished,
}
}
pub async fn sleep(duration: Duration) {
let ms = duration.as_millis().min(i32::MAX as u128) as i32;
let promise = js_sys::Promise::new(&mut |resolve, _reject| {
let global = js_sys::global();
let set_timeout = js_sys::Reflect::get(&global, &"setTimeout".into())
.ok()
.and_then(|f| f.dyn_into::<js_sys::Function>().ok());
match set_timeout {
Some(f) => {
let _ = f.call2(&global, &resolve, &ms.into());
}
None => {
let _ = resolve.call0(&global);
}
}
});
let _ = wasm_bindgen_futures::JsFuture::from(promise).await;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Elapsed;
impl fmt::Display for Elapsed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("deadline has elapsed")
}
}
impl std::error::Error for Elapsed {}
pub async fn timeout<F: Future>(duration: Duration, future: F) -> Result<F::Output, Elapsed> {
use futures::future::{select, Either};
let future = std::pin::pin!(future);
let timer = std::pin::pin!(sleep(duration));
match select(future, timer).await {
Either::Left((value, _)) => Ok(value),
Either::Right(_) => Err(Elapsed),
}
}
use wasm_bindgen::JsCast;
}