use std::{
any::Any,
future::Future,
panic::{self, AssertUnwindSafe},
pin::Pin,
sync::{Arc, Condvar, Mutex},
task::{Context, Poll},
};
use crate::{Threadpool, atomic_waker::AtomicWaker, task::OwnedTask};
struct SpawnFutureData<T> {
condvar: Condvar,
waker: AtomicWaker,
result: Mutex<Option<Result<T, Box<dyn Any + Send>>>>,
}
enum State<F, R> {
Init(F),
Running(Arc<SpawnFutureData<R>>),
Done,
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct SpawnFuture<'pool, F, R> {
pool: &'pool Threadpool,
state: State<F, R>,
}
unsafe impl<T: Send> Send for SpawnFutureData<T> {}
unsafe impl<T: Send> Sync for SpawnFutureData<T> {}
impl<'pool, F, R> SpawnFuture<'pool, F, R>
where
F: FnOnce() -> R + Send,
R: Send,
{
pub(crate) fn new(pool: &'pool Threadpool, f: F) -> Self {
SpawnFuture {
pool,
state: State::Init(f),
}
}
}
impl<F, T> Future for SpawnFuture<'_, F, T>
where
F: FnOnce() -> T + Send,
T: Send,
{
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let this = unsafe { self.as_mut().get_unchecked_mut() };
match this.state {
State::Init(_) => {
let State::Init(task) = std::mem::replace(&mut this.state, State::Done) else {
unreachable!()
};
let data = Arc::new(SpawnFutureData {
condvar: Condvar::new(),
result: Mutex::new(None),
waker: AtomicWaker::new(),
});
data.waker.register(cx.waker());
let data_clone = data.clone();
let task = OwnedTask::new(move || {
{
let mut lock = data_clone.result.lock().unwrap();
let res = panic::catch_unwind(AssertUnwindSafe(task));
*lock = Some(res);
}
data_clone.waker.wake();
data_clone.condvar.notify_one();
});
unsafe {
this.pool.data.injector.push(task.erase_lifetime());
}
if let Some(thread) = this.pool.data.parked_threads.pop() {
thread.unpark();
}
this.state = State::Running(data);
}
State::Running(ref data) => {
let data = data.clone();
match data.result.try_lock() {
Ok(mut guard) => {
if let Some(res) = guard.take() {
drop(guard);
this.state = State::Done;
return Poll::Ready(
res.unwrap_or_else(|p| panic::resume_unwind(p)),
);
} else {
drop(guard);
data.waker.register(cx.waker());
return Poll::Pending;
}
}
Err(_) => {
data.waker.register(cx.waker());
return Poll::Pending;
}
}
}
State::Done => {
panic!("SpawnFuture polled after completion")
}
};
}
}
}
impl<F, T> Drop for SpawnFuture<'_, F, T> {
fn drop(&mut self) {
if let State::Running(ref data) = self.state {
let guard = data.result.lock().unwrap();
let _guard = data.condvar.wait_while(guard, |x| x.is_none()).unwrap();
}
}
}