use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
struct ProcessSlot<T> {
result: Option<T>,
waker: Option<Waker>,
}
pub struct ProcessHandle<T> {
slot: Rc<RefCell<ProcessSlot<T>>>,
}
impl<T> std::fmt::Debug for ProcessHandle<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut d = f.debug_struct("ProcessHandle");
if let Ok(slot) = self.slot.try_borrow() {
d.field("ready", &slot.result.is_some());
}
d.finish_non_exhaustive()
}
}
impl<T: 'static> Future for ProcessHandle<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
let mut slot = self.slot.borrow_mut();
if let Some(value) = slot.result.take() {
return Poll::Ready(value);
}
let waker = cx.waker();
match &slot.waker {
Some(existing) if existing.will_wake(waker) => {}
_ => slot.waker = Some(waker.clone()),
}
Poll::Pending
}
}
impl<T: 'static> ProcessHandle<T> {
pub async fn discard(self) {
let _ = self.await;
}
}
#[allow(clippy::type_complexity)]
pub(crate) fn spawn_with_handle<F>(
future: F,
) -> (Pin<Box<dyn Future<Output = ()>>>, ProcessHandle<F::Output>)
where
F: Future + 'static,
F::Output: 'static,
{
let slot = Rc::new(RefCell::new(ProcessSlot {
result: None,
waker: None,
}));
let slot_for_wrapper = Rc::clone(&slot);
let wrapped = async move {
let result = future.await;
let mut s = slot_for_wrapper.borrow_mut();
s.result = Some(result);
if let Some(w) = s.waker.take() {
w.wake();
}
};
(Box::pin(wrapped), ProcessHandle { slot })
}