partway towards commands
I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
92
orchid-async-utils/src/task_future.rs
Normal file
92
orchid-async-utils/src/task_future.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
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<LocalBoxFuture<'static, Box<dyn Any>>>,
|
||||
result: Option<Box<dyn Any>>,
|
||||
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<RefCell<State>>);
|
||||
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<Self::Output> {
|
||||
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<T: 'static>(Rc<RefCell<State>>, PhantomData<T>);
|
||||
impl<T: 'static> Handle<T> {
|
||||
/// Immediately stop working on this task, and return the result if it has
|
||||
/// already finished
|
||||
pub fn abort(&self) -> Option<T> {
|
||||
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<F: Future<Output: 'static> + 'static>(f: F) -> (Pollable, Handle<F::Output>) {
|
||||
let dyn_future = Box::pin(async { Box::new(f.await) as Box<dyn Any> });
|
||||
let state = Rc::new(RefCell::new(State {
|
||||
result: None,
|
||||
work: Some(dyn_future),
|
||||
waker: Waker::noop().clone(),
|
||||
}));
|
||||
(Pollable(state.clone()), Handle(state, PhantomData))
|
||||
}
|
||||
Reference in New Issue
Block a user