#![feature(async_fn_traits, unboxed_closures)]
mod future_wrappers;
use crate::future_wrappers::{SendPtr, WriteOutput};
use std::{
any::Any, error::Error, fmt, future::Future, marker::PhantomData, mem::MaybeUninit,
ptr::NonNull,
};
use tokio::task::JoinSet;
type TokioJoinError = tokio::task::JoinError;
#[derive(Default)]
pub struct ScopedJoinSet<'env, TaskResult>
where
TaskResult: 'env + Send,
{
join_set: JoinSet<SendPtr>,
_marker: PhantomData<&'env TaskResult>,
}
impl<'env, TaskResult> ScopedJoinSet<'env, TaskResult>
where
TaskResult: 'env + Send,
{
unsafe fn new() -> Self {
Self {
join_set: JoinSet::new(),
_marker: PhantomData,
}
}
pub fn spawn<F>(&mut self, task: F)
where
F: Future<Output = TaskResult> + Send + 'env,
TaskResult: Send,
{
let output_slot: NonNull<MaybeUninit<TaskResult>> =
Box::leak(Box::new(MaybeUninit::uninit())).into();
let wrapped_task = unsafe { WriteOutput::new(task, output_slot.cast()) };
let static_task = unsafe { wrapped_task.into_static() };
self.join_set.spawn(static_task);
}
pub fn is_empty(&self) -> bool {
self.join_set.is_empty()
}
pub fn len(&self) -> usize {
self.join_set.len()
}
pub async fn join_next(&mut self) -> Option<Result<TaskResult, JoinError>> {
match self.join_set.join_next().await? {
Ok(output_ptr) => {
let typed_ptr: NonNull<TaskResult> = output_ptr.cast();
let output_value = unsafe { *Box::from_raw(typed_ptr.as_ptr()) };
Some(Ok(output_value))
}
Err(join_error) => Some(Err(join_error.into())),
}
}
pub fn try_join_next(&mut self) -> Option<Result<TaskResult, JoinError>> {
match self.join_set.try_join_next()? {
Ok(output_ptr) => {
let typed_ptr: NonNull<TaskResult> = output_ptr.cast();
let output_value = unsafe { *Box::from_raw(typed_ptr.as_ptr()) };
Some(Ok(output_value))
}
Err(join_error) => Some(Err(join_error.into())),
}
}
pub fn abort_all(&mut self) {
self.join_set.abort_all();
}
pub async fn join_all(&mut self) -> Vec<Result<TaskResult, JoinError>> {
let mut results = Vec::with_capacity(self.len());
while let Some(result) = self.join_next().await {
results.push(result);
}
results
}
}
unsafe impl<'env, TaskResult: Send> Send for ScopedJoinSet<'env, TaskResult> {}
#[derive(Debug)]
pub enum JoinError {
Cancelled,
Panicked(Box<dyn Any + Send + 'static>),
}
impl fmt::Display for JoinError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
JoinError::Cancelled => write!(formatter, "task was cancelled"),
JoinError::Panicked(_) => write!(formatter, "task panicked"),
}
}
}
impl Error for JoinError {}
impl From<TokioJoinError> for JoinError {
fn from(error: TokioJoinError) -> Self {
if error.is_cancelled() {
JoinError::Cancelled
} else if error.is_panic() {
JoinError::Panicked(error.into_panic())
} else {
JoinError::Cancelled
}
}
}
pub struct Scope<'scope, 'env, TaskResult>
where
TaskResult: Send + 'env,
{
task_set: &'scope mut ScopedJoinSet<'env, TaskResult>,
}
impl<'scope, 'env, TaskResult> Scope<'scope, 'env, TaskResult>
where
TaskResult: Send + 'env,
{
pub fn spawn<F>(&mut self, future: F)
where
F: Future<Output = TaskResult> + Send + 'env,
{
self.task_set.spawn(future);
}
pub async fn join_next(&mut self) -> Option<Result<TaskResult, JoinError>> {
self.task_set.join_next().await
}
pub fn try_join_next(&mut self) -> Option<Result<TaskResult, JoinError>> {
self.task_set.try_join_next()
}
pub fn is_empty(&self) -> bool {
self.task_set.is_empty()
}
pub fn len(&self) -> usize {
self.task_set.len()
}
pub fn abort_all(&mut self) {
self.task_set.abort_all();
}
pub async fn join_all(&mut self) -> Vec<Result<TaskResult, JoinError>> {
self.task_set.join_all().await
}
}
struct AbortOnDrop;
impl Drop for AbortOnDrop {
fn drop(&mut self) {
eprintln!("Tried to cancel task scope");
std::process::abort();
}
}
pub async fn scope<'env, TaskResult, Closure, ClosureResult>(closure: Closure) -> ClosureResult
where
TaskResult: Send + 'env,
Closure: for<'s> AsyncFnOnce(&'s mut Scope<'s, 'env, TaskResult>) -> ClosureResult,
{
let guard = AbortOnDrop;
let mut task_set = unsafe { ScopedJoinSet::<TaskResult>::new() };
let mut scope_handle = Scope {
task_set: &mut task_set,
};
let result = closure(&mut scope_handle).await;
task_set.abort_all();
task_set.join_all().await;
drop(task_set);
std::mem::forget(guard);
result
}