use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use futures_channel::oneshot;
use futures_util::future::{AbortHandle, Abortable};
use crate::Runtime;
pub struct Task<T> {
abort: AbortHandle,
output: oneshot::Receiver<T>,
finished: Arc<AtomicBool>,
}
impl<T> Task<T> {
pub fn abort(&self) {
self.abort.abort();
}
pub fn is_finished(&self) -> bool {
self.finished.load(Ordering::Acquire)
}
}
impl<T> Future for Task<T> {
type Output = Option<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
match Pin::new(&mut self.output).poll(cx) {
Poll::Ready(output) => {
self.finished.store(true, Ordering::Release);
Poll::Ready(output.ok())
}
Poll::Pending => Poll::Pending,
}
}
}
impl<T> fmt::Debug for Task<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Task")
.field("finished", &self.is_finished())
.finish_non_exhaustive()
}
}
struct Finish(Arc<AtomicBool>);
impl Drop for Finish {
fn drop(&mut self) {
self.0.store(true, Ordering::Release);
}
}
pub fn spawn<T, F>(runtime: &dyn Runtime, future: F) -> Task<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
let (output_tx, output_rx) = oneshot::channel();
let (abort, registration) = AbortHandle::new_pair();
let finished = Arc::new(AtomicBool::new(false));
let finish = Finish(Arc::clone(&finished));
let run = Abortable::new(
async move {
let value = future.await;
drop(finish);
let _ = output_tx.send(value);
},
registration,
);
runtime.spawn(Box::pin(async move {
let _ = run.await;
}));
Task {
abort,
output: output_rx,
finished,
}
}