#![feature(ptr_metadata)]
mod future_wrappers;
use crate::future_wrappers::{split_spawn_task, FutureHolder};
use std::{any::Any, collections::HashMap, error::Error, fmt, future::Future, marker::PhantomData};
use tokio::task::{Id, JoinSet};
type TokioJoinError = tokio::task::JoinError;
#[derive(Default)]
pub struct ScopedJoinSet<'scope, T>
where
T: 'scope + Send,
{
join_set: JoinSet<()>,
holders: HashMap<Id, FutureHolder<'scope, T>>,
_marker: PhantomData<T>,
}
impl<'scope, T> ScopedJoinSet<'scope, T>
where
T: 'scope + Send,
{
pub fn new() -> Self {
Self {
join_set: JoinSet::new(),
holders: HashMap::new(),
_marker: PhantomData,
}
}
pub fn spawn<F>(&mut self, task: F)
where
F: Future<Output = T> + Send + 'scope,
T: Send,
{
let (id, holder) = split_spawn_task(&mut self.join_set, task);
self.holders.insert(id, holder);
}
pub fn is_empty(&self) -> bool {
self.join_set.is_empty()
}
pub fn len(&self) -> usize {
self.holders.len()
}
pub async fn join_next(&mut self) -> Option<Result<T, JoinError>> {
match self.join_set.join_next_with_id().await? {
Ok((id, _)) => {
let holder = self.holders.remove(&id)?;
let output = holder.get_output().ok_or(JoinError::Cancelled);
Some(output)
}
Err(error) => {
self.holders.remove(&error.id());
Some(Err(error.into()))
}
}
}
pub fn try_join_next(&mut self) -> Option<Result<T, JoinError>> {
match self.join_set.try_join_next_with_id()? {
Ok((id, _)) => {
let holder = self.holders.remove(&id)?;
let output = holder.get_output().ok_or(JoinError::Cancelled);
Some(output)
}
Err(error) => {
self.holders.remove(&error.id());
Some(Err(error.into()))
}
}
}
pub async fn join_all(mut self) -> Vec<Result<T, JoinError>> {
let mut results = Vec::with_capacity(self.len());
while let Some(res) = self.join_next().await {
results.push(res);
}
results
}
pub fn abort_all(&mut self) {
for holder in self.holders.values_mut() {
holder.abort();
}
}
pub async fn shutdown(mut self) -> Vec<Result<T, JoinError>> {
self.abort_all();
self.join_all().await
}
}
unsafe impl<'scope, T: Send> Send for ScopedJoinSet<'scope, T> {}
#[derive(Debug)]
pub enum JoinError {
Cancelled,
Panicked(Box<dyn Any + Send + 'static>),
}
impl fmt::Display for JoinError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JoinError::Cancelled => write!(f, "task was cancelled"),
JoinError::Panicked(_) => write!(f, "task panicked"),
}
}
}
impl Error for JoinError {}
impl JoinError {
pub fn is_cancelled(&self) -> bool {
matches!(self, JoinError::Cancelled)
}
pub fn is_panic(&self) -> bool {
matches!(self, JoinError::Panicked(_))
}
}
impl From<TokioJoinError> for JoinError {
fn from(err: TokioJoinError) -> Self {
if err.is_cancelled() {
JoinError::Cancelled
} else if err.is_panic() {
JoinError::Panicked(err.into_panic())
} else {
JoinError::Cancelled
}
}
}