use std::{cell::RefCell, future::Future, marker::PhantomData, pin::Pin};
use tokio::task::{JoinError, JoinHandle};
pub struct Token<'scope, 'spawner, O> {
list: &'spawner RefCell<Vec<JoinHandle<O>>>,
_phantom: PhantomData<&'scope mut &'scope ()>,
}
pub struct Spawner<'scope, 'spawner, T, O> {
list: &'spawner RefCell<Vec<JoinHandle<O>>>,
used: T,
_phantom: PhantomData<&'scope mut &'scope ()>,
}
pub async fn scope<'scope, F, O>(f: F) -> Vec<Result<O, JoinError>>
where
for<'spawner> F: FnOnce(Token<'scope, 'spawner, O>),
O: Send + 'static,
{
struct ScopeGuard(());
impl ScopeGuard {
fn forget(self) {
#[allow(clippy::disallowed_methods)]
std::mem::forget(self);
}
}
impl Drop for ScopeGuard {
fn drop(&mut self) {
std::process::abort();
}
}
let guard = ScopeGuard(());
let list = RefCell::new(Vec::new());
let token = Token {
list: &list,
_phantom: PhantomData,
};
f(token);
let list = RefCell::into_inner(list);
let mut output = Vec::with_capacity(list.len());
for j in list {
output.push(j.await);
}
guard.forget();
output
}
impl<'scope, 'spawner, O> Token<'scope, 'spawner, O> {
pub unsafe fn used<T: 'scope>(&self, used: T) -> Spawner<'scope, 'spawner, T, O> {
Spawner {
list: self.list,
used,
_phantom: PhantomData,
}
}
}
impl<'scope, T, O> Spawner<'scope, '_, T, O> {
pub fn spawn<F, Fut>(self, f: F)
where
F: FnOnce(T) -> Fut + 'static,
Fut: Future<Output = O> + Send + 'scope,
T: Send + Sync + 'scope,
O: Send + 'static,
{
let fut = f(self.used);
let fut: Pin<Box<dyn Future<Output = O> + Send + 'scope>> = Box::pin(fut);
let fut: Pin<Box<dyn Future<Output = O> + Send + 'static>> =
unsafe { std::mem::transmute(fut) };
let j = rspack_tasks::spawn_in_compiler_context(fut);
self.list.borrow_mut().push(j);
}
}