#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations)]
use crate::task::task_impl;
use crate::task::JoinHandle;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
pub type Runnable = task_impl::Task<()>;
#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
#[derive(Debug)]
pub struct Task<T>(Option<JoinHandle<T, ()>>);
impl<T> Task<T> {
pub(crate) fn detach(mut self) -> JoinHandle<T, ()> {
self.0.take().unwrap()
}
pub(crate) async fn cancel(self) -> Option<T> {
let mut task = self;
let handle = task.0.take().unwrap();
handle.cancel();
handle.await
}
}
impl<T> Drop for Task<T> {
fn drop(&mut self) {
if let Some(handle) = &self.0 {
handle.cancel();
}
}
}
impl<T> Future for Task<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.0.as_mut().unwrap()).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(output) => Poll::Ready(output.expect("task has failed")),
}
}
}
#[derive(Debug)]
struct LocalQueue {
queue: RefCell<VecDeque<Runnable>>,
}
impl LocalQueue {
fn new() -> Rc<Self> {
Rc::new(LocalQueue {
queue: RefCell::new(VecDeque::new()),
})
}
fn push(&self, runnable: Runnable) {
self.queue.borrow_mut().push_back(runnable);
}
fn pop(&self) -> Option<Runnable> {
self.queue.borrow_mut().pop_front()
}
}
#[derive(Debug)]
pub struct LocalExecutor {
local_queue: Rc<LocalQueue>,
callback: Callback,
_marker: PhantomData<Rc<()>>,
}
impl UnwindSafe for LocalExecutor {}
impl RefUnwindSafe for LocalExecutor {}
impl LocalExecutor {
pub(crate) fn new(notify: impl Fn() + 'static) -> LocalExecutor {
LocalExecutor {
local_queue: LocalQueue::new(),
callback: Callback::new(notify),
_marker: PhantomData,
}
}
pub(crate) fn spawn<T: 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T> {
let callback = self.callback.clone();
let queue_weak = Rc::downgrade(&self.local_queue);
let schedule = move |runnable: Runnable| {
let queue = queue_weak.upgrade().unwrap();
queue.push(runnable);
callback.call();
};
let (runnable, handle) = task_impl::spawn_local(future, schedule, ());
runnable.schedule();
Task(Some(handle))
}
pub(crate) fn get_task(&self) -> Option<Runnable> {
self.local_queue.pop()
}
pub(crate) fn is_active(&self) -> bool {
!self.local_queue.queue.borrow().is_empty()
}
}
#[derive(Clone)]
struct Callback(Rc<dyn Fn()>);
impl Callback {
fn new(f: impl Fn() + 'static) -> Callback {
Callback(Rc::new(f))
}
fn call(&self) {
(self.0)();
}
}
impl fmt::Debug for Callback {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("<callback>").finish()
}
}