#![forbid(unsafe_code)]
#![warn(missing_docs, missing_debug_implementations)]
use crate::{
executor::{maybe_activate, TaskQueue},
task::{task_impl, JoinHandle},
Latency,
};
use std::{
cell::RefCell,
collections::VecDeque,
future::Future,
marker::PhantomData,
panic::{RefUnwindSafe, UnwindSafe},
pin::Pin,
rc::Rc,
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,
}
}
fn spawn<T>(
&self,
executor_id: usize,
tq: Rc<RefCell<TaskQueue>>,
future: impl Future<Output = T>,
) -> (Runnable, JoinHandle<T>) {
let latency_matters = match tq.borrow().io_requirements.latency_req {
Latency::Matters(_) => true,
Latency::NotImportant => false,
};
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);
}
};
task_impl::spawn_local(executor_id, future, schedule, latency_matters)
}
pub(crate) fn spawn_and_run<T>(
&self,
executor_id: usize,
tq: Rc<RefCell<TaskQueue>>,
future: impl Future<Output = T>,
) -> Task<T> {
let (runnable, handle) = self.spawn(executor_id, tq, future);
runnable.run_right_away();
Task(Some(handle))
}
pub(crate) fn spawn_and_schedule<T>(
&self,
executor_id: usize,
tq: Rc<RefCell<TaskQueue>>,
future: impl Future<Output = T>,
) -> Task<T> {
let (runnable, handle) = self.spawn(executor_id, tq, future);
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()
}
}