#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations)]
use crate::executor::{maybe_activate, TaskQueue};
use crate::task::task_impl;
use crate::task::JoinHandle;
use std::cell::RefCell;
use std::collections::VecDeque;
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(crate) type Runnable = task_impl::Task;
#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
#[derive(Debug)]
pub(crate) 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> {
Pin::new(&mut self.0.as_mut().unwrap())
.poll(cx)
.map(|output| output.expect("task has failed"))
}
}
#[derive(Debug)]
struct LocalQueue {
queue: RefCell<VecDeque<Runnable>>,
}
impl LocalQueue {
fn new() -> Self {
LocalQueue {
queue: RefCell::new(VecDeque::new()),
}
}
pub(crate) fn push(&self, runnable: Runnable) {
self.queue.borrow_mut().push_back(runnable);
}
pub(crate) fn pop(&self) -> Option<Runnable> {
self.queue.borrow_mut().pop_front()
}
}
#[derive(Debug)]
pub(crate) struct LocalExecutor {
local_queue: LocalQueue,
_marker: PhantomData<Rc<()>>,
}
impl UnwindSafe for LocalExecutor {}
impl RefUnwindSafe for LocalExecutor {}
impl LocalExecutor {
pub(crate) fn new() -> LocalExecutor {
LocalExecutor {
local_queue: LocalQueue::new(),
_marker: PhantomData,
}
}
pub(crate) fn spawn<T>(
&self,
tq: Rc<RefCell<TaskQueue>>,
future: impl Future<Output = T>,
) -> Task<T> {
let tq = Rc::downgrade(&tq);
let schedule = move |runnable: Runnable| {
let tq = tq.upgrade();
if let Some(tq) = tq {
{
let queue = tq.borrow();
queue.ex.local_queue.push(runnable);
}
maybe_activate(tq);
}
};
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()
}
}