use std::any::Any;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::backend::{Active, Backend};
pub enum JoinError {
Panic(Box<dyn Any + Send>),
Cancelled,
}
impl fmt::Debug for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JoinError::Panic(_) => f.write_str("JoinError::Panic(..)"),
JoinError::Cancelled => f.write_str("JoinError::Cancelled"),
}
}
}
impl fmt::Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JoinError::Panic(_) => f.write_str("task panicked"),
JoinError::Cancelled => f.write_str("task was cancelled"),
}
}
}
impl std::error::Error for JoinError {}
type CatchUnwindResult<T> = Result<T, Box<dyn Any + Send>>;
pub struct JoinHandle<T> {
#[cfg(feature = "rt-tokio")]
pub(crate) inner: tokio::task::JoinHandle<CatchUnwindResult<T>>,
#[cfg(feature = "rt-async-std")]
pub(crate) inner: Option<async_std::task::JoinHandle<CatchUnwindResult<T>>>,
#[cfg(feature = "rt-smol")]
pub(crate) inner: Option<smol::Task<CatchUnwindResult<T>>>,
}
impl<T> Unpin for JoinHandle<T> {}
impl<T> Future for JoinHandle<T> {
type Output = Result<T, JoinError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
#[cfg(feature = "rt-tokio")]
{
match Pin::new(&mut this.inner).poll(cx) {
Poll::Ready(Ok(Ok(val))) => Poll::Ready(Ok(val)),
Poll::Ready(Ok(Err(panic_payload))) => {
Poll::Ready(Err(JoinError::Panic(panic_payload)))
}
Poll::Ready(Err(_join_err)) => Poll::Ready(Err(JoinError::Cancelled)),
Poll::Pending => Poll::Pending,
}
}
#[cfg(feature = "rt-async-std")]
{
let handle = this
.inner
.as_mut()
.expect("JoinHandle polled after completion");
match Pin::new(handle).poll(cx) {
Poll::Ready(Ok(val)) => {
this.inner = None;
Poll::Ready(Ok(val))
}
Poll::Ready(Err(panic_payload)) => {
this.inner = None;
Poll::Ready(Err(JoinError::Panic(panic_payload)))
}
Poll::Pending => Poll::Pending,
}
}
#[cfg(feature = "rt-smol")]
{
let task = this
.inner
.as_mut()
.expect("JoinHandle polled after completion");
match Pin::new(task).poll(cx) {
Poll::Ready(Ok(val)) => {
this.inner = None;
Poll::Ready(Ok(val))
}
Poll::Ready(Err(panic_payload)) => {
this.inner = None;
Poll::Ready(Err(JoinError::Panic(panic_payload)))
}
Poll::Pending => Poll::Pending,
}
}
}
}
#[cfg(feature = "rt-smol")]
impl<T> Drop for JoinHandle<T> {
fn drop(&mut self) {
if let Some(task) = self.inner.take() {
task.detach();
}
}
}
pub fn spawn<T: Send + 'static>(
fut: impl Future<Output = T> + Send + 'static,
) -> JoinHandle<T> {
Active::spawn(fut)
}
#[macro_export]
macro_rules! go {
($expr:expr) => {
$crate::spawn($expr)
};
}