#![no_std]
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::task::{Context, Poll};
use alloc::rc::Rc;
use async_task::Runnable;
pub use async_task::{FallibleTask, Task};
use atomic_waker::AtomicWaker;
use futures_lite::FutureExt;
#[cfg(not(feature = "portable-atomic"))]
use alloc::sync::Arc;
#[cfg(feature = "portable-atomic")]
use portable_atomic_util::Arc;
use once_cell::sync::OnceCell;
#[cfg(feature = "std")]
pub use futures_lite::future::block_on;
pub use queue::*;
extern crate alloc;
mod queue;
pub struct Executor<'a, Q = BoundQueue> {
state: OnceCell<Arc<State<Q>>>,
queue_ctor: fn() -> Q,
_marker: PhantomData<core::cell::UnsafeCell<&'a ()>>,
}
impl<'a, Q: ExecutorQueue> Executor<'a, Q> {
pub const fn new() -> Self {
Self::new_with(Q::new)
}
pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
Self {
state: OnceCell::new(),
queue_ctor,
_marker: PhantomData,
}
}
pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
where
F: Future + Send + 'a,
F::Output: Send + 'a,
{
unsafe { self.spawn_unchecked(fut) }
}
pub fn try_tick(&self) -> bool {
if let Some(runnable) = self.try_runnable() {
runnable.run();
true
} else {
false
}
}
pub async fn tick(&self) {
self.runnable().await.run();
}
pub async fn run<F>(&self, fut: F) -> F::Output
where
F: Future,
{
unsafe { self.run_unchecked(fut).await }
}
async fn runnable(&self) -> Runnable {
poll_fn(|ctx| self.poll_runnable(ctx)).await
}
fn poll_runnable(&self, ctx: &Context<'_>) -> Poll<Runnable> {
self.state().waker.register(ctx.waker());
if let Some(runnable) = self.try_runnable() {
Poll::Ready(runnable)
} else {
Poll::Pending
}
}
fn try_runnable(&self) -> Option<Runnable> {
self.state().queue.pop()
}
unsafe fn spawn_unchecked<F>(&self, fut: F) -> Task<F::Output>
where
F: Future,
{
let schedule = {
let state = self.state().clone();
move |runnable| {
state.queue.push(runnable);
if let Some(waker) = state.waker.take() {
waker.wake();
}
}
};
let (runnable, task) = unsafe { async_task::spawn_unchecked(fut, schedule) };
runnable.schedule();
task
}
async unsafe fn run_unchecked<F>(&self, fut: F) -> F::Output
where
F: Future,
{
let run_forever = async {
loop {
self.tick().await;
}
};
run_forever.or(fut).await
}
fn state(&self) -> &Arc<State<Q>> {
self.state
.get_or_init(|| Arc::new(State::new((self.queue_ctor)())))
}
}
impl<Q: ExecutorQueue> Default for Executor<'_, Q> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<Q: Send> Send for Executor<'_, Q> {}
unsafe impl<Q: Sync> Sync for Executor<'_, Q> {}
pub struct LocalExecutor<'a, Q = BoundQueue> {
executor: Executor<'a, Q>,
_marker: PhantomData<Rc<()>>,
}
impl<'a, Q: ExecutorQueue> LocalExecutor<'a, Q> {
pub const fn new() -> Self {
Self::new_with(Q::new)
}
pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
Self {
executor: Executor::<Q>::new_with(queue_ctor),
_marker: PhantomData,
}
}
pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
where
F: Future + 'a,
F::Output: 'a,
{
unsafe { self.executor.spawn_unchecked(fut) }
}
pub fn try_tick(&self) -> bool {
self.executor.try_tick()
}
pub async fn tick(&self) {
self.executor.tick().await
}
pub async fn run<F>(&self, fut: F) -> F::Output
where
F: Future,
{
unsafe { self.executor.run_unchecked(fut) }.await
}
}
impl<'a, Q: ExecutorQueue> Default for LocalExecutor<'a, Q> {
fn default() -> Self {
Self::new()
}
}
struct State<Q> {
queue: Q,
waker: AtomicWaker,
}
impl<Q> State<Q> {
const fn new(queue: Q) -> Self {
Self {
queue,
waker: AtomicWaker::new(),
}
}
}