#[cfg(not(target_arch = "wasm32"))]
mod native {
use std::future::Future;
pub use tokio::task::{JoinError, JoinHandle};
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
tokio::spawn(future)
}
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
F::Output: 'static,
{
tokio::task::spawn_local(future)
}
}
#[cfg(target_arch = "wasm32")]
mod web {
use futures_channel::oneshot;
use futures_util::future::{AbortHandle, Abortable};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::task::{Context, Poll};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct JoinError;
impl JoinError {
#[must_use]
pub const fn is_cancelled(&self) -> bool {
true
}
}
impl fmt::Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("task was cancelled")
}
}
impl std::error::Error for JoinError {}
pub struct JoinHandle<T> {
abort: AbortHandle,
result: oneshot::Receiver<Result<T, JoinError>>,
finished: Arc<AtomicBool>,
}
impl<T> JoinHandle<T> {
pub fn abort(&self) {
self.abort.abort();
}
#[must_use]
pub fn is_finished(&self) -> bool {
self.finished.load(Ordering::Acquire)
}
}
impl<T> Future for JoinHandle<T> {
type Output = Result<T, JoinError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.result).poll(cx) {
Poll::Ready(Ok(result)) => Poll::Ready(result),
Poll::Ready(Err(_)) => Poll::Ready(Err(JoinError)),
Poll::Pending => Poll::Pending,
}
}
}
impl<T> fmt::Debug for JoinHandle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("JoinHandle")
.field("finished", &self.is_finished())
.finish()
}
}
fn parts<T>() -> (
futures_util::future::AbortRegistration,
oneshot::Sender<Result<T, JoinError>>,
JoinHandle<T>,
Arc<AtomicBool>,
) {
let (abort, registration) = AbortHandle::new_pair();
let (send, result) = oneshot::channel();
let finished = Arc::new(AtomicBool::new(false));
let handle = JoinHandle {
abort,
result,
finished: finished.clone(),
};
(registration, send, handle, finished)
}
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
let (registration, send, handle, finished) = parts();
web_task::spawn(async move {
let result = Abortable::new(future, registration)
.await
.map_err(|_| JoinError);
finished.store(true, Ordering::Release);
let _ = send.send(result);
})
.detach();
handle
}
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
where
F: Future + 'static,
F::Output: 'static,
{
let (registration, send, handle, finished) = parts();
web_task::spawn_local(async move {
let result = Abortable::new(future, registration)
.await
.map_err(|_| JoinError);
finished.store(true, Ordering::Release);
let _ = send.send(result);
})
.detach();
handle
}
}
#[cfg(not(target_arch = "wasm32"))]
pub use native::*;
#[cfg(target_arch = "wasm32")]
pub use web::*;