use std::any::Any; use std::cell::RefCell; use std::marker::PhantomData; use std::pin::Pin; use std::rc::Rc; use std::task::{Context, Poll, Waker}; use futures::future::{FusedFuture, LocalBoxFuture}; struct State { work: Option>>, result: Option>, waker: Waker, } /// A fused future that can be passed to a non-polymorphic executor that doesn't /// process results and doesn't return handles pub struct Pollable(Rc>); impl FusedFuture for Pollable { fn is_terminated(&self) -> bool { let g = self.0.borrow(); g.work.is_none() || g.result.is_some() } } impl Future for Pollable { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut g = self.0.borrow_mut(); match &mut *g { State { result: Some(_), .. } | State { work: None, .. } => Poll::Ready(()), State { work: Some(work), waker, result } => match work.as_mut().poll(cx) { Poll::Pending => { waker.clone_from(cx.waker()); Poll::Pending }, Poll::Ready(val) => { *result = Some(val); g.work = None; Poll::Ready(()) }, }, } } } /// An object that can be used to inspect the state of the task pub struct Handle(Rc>, PhantomData); impl Handle { /// Immediately stop working on this task, and return the result if it has /// already finished pub fn abort(&self) -> Option { let mut g = self.0.borrow_mut(); g.work.take(); match g.result.take() { Some(val) => Some(*val.downcast().expect("Mismatch between type of future and handle")), None => { g.waker.wake_by_ref(); None }, } } /// Determine if there's any more work to do on this task pub fn is_finished(&self) -> bool { let g = self.0.borrow(); g.result.is_some() || g.work.is_none() } /// "finish" the freestanding task, and return the future instead pub async fn join(self) -> T { let work = { let mut g = self.0.borrow_mut(); if let Some(val) = g.result.take() { return *val.downcast().expect("Mistmatch between type of future and handle"); } g.waker.wake_by_ref(); g.work.take().expect("Attempted to join task that was already aborted") }; *work.await.downcast().expect("Mismatch between type of future and handle") } } /// Split a future into an object that can be polled and one that returns /// information on its progress and its result. The first one can be passed to /// an executor or localset, the second can be used to manage it pub fn to_task + 'static>(f: F) -> (Pollable, Handle) { let dyn_future = Box::pin(async { Box::new(f.await) as Box }); let state = Rc::new(RefCell::new(State { result: None, work: Some(dyn_future), waker: Waker::noop().clone(), })); (Pollable(state.clone()), Handle(state, PhantomData)) }