use alloc::{sync::Arc, task::Wake};
use core::{
future::poll_fn,
pin::pin,
task::{Context, Poll, Waker},
};
use crate::{
AxTaskRef, WeakAxTaskRef, current, current_run_queue, select_wake_run_queue,
sync::{PreemptIrqSaveState, SpinLock},
};
mod poll;
pub use poll::*;
pub(crate) mod time;
pub use time::*;
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
pub enum TaskError {
#[error(transparent)]
Interrupted(#[from] Interrupted),
#[error(transparent)]
Elapsed(#[from] Elapsed),
#[error("task operation would block")]
WouldBlock,
#[error(transparent)]
Irq(#[from] ax_hal::irq::IrqError),
}
pub type TaskResult<T = ()> = Result<T, TaskError>;
pub trait PollIoError {
fn is_would_block(&self) -> bool;
fn interrupted(error: Interrupted) -> Self;
}
impl PollIoError for TaskError {
fn is_would_block(&self) -> bool {
matches!(self, Self::WouldBlock)
}
fn interrupted(error: Interrupted) -> Self {
error.into()
}
}
struct AxWaker {
task: WeakAxTaskRef,
woke: SpinLock<bool>,
}
impl AxWaker {
fn new(task: &AxTaskRef) -> Arc<Self> {
Arc::new(AxWaker {
task: Arc::downgrade(task),
woke: SpinLock::new(false),
})
}
}
impl Wake for AxWaker {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
if let Some(task) = self.task.upgrade() {
let mut rq = select_wake_run_queue::<PreemptIrqSaveState>(&task);
*self.woke.lock_irqsave() = true;
rq.unblock_task(task, true);
}
}
}
#[track_caller]
pub fn block_on<F: IntoFuture>(f: F) -> F::Output {
crate::api::might_sleep();
let mut fut = pin!(f.into_future());
let curr = current();
let task = curr.clone();
let axwaker = AxWaker::new(&task);
let waker = Waker::from(axwaker.clone());
let mut cx = Context::from_waker(&waker);
loop {
match fut.as_mut().poll(&mut cx) {
Poll::Pending => {
if task.interrupted() {
crate::yield_now();
continue;
}
let mut rq = current_run_queue::<PreemptIrqSaveState>();
let mut woke = axwaker.woke.lock_irqsave();
if !*woke {
rq.future_blocked_resched(woke);
} else {
*woke = false;
drop(woke);
drop(rq);
crate::yield_now();
}
}
Poll::Ready(output) => break output,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
#[error("task wait was interrupted")]
pub struct Interrupted;
pub async fn interruptible<F: IntoFuture>(f: F) -> Result<F::Output, Interrupted> {
let mut f = pin!(f.into_future());
let curr = current();
poll_fn(|cx| {
if curr.poll_interrupt(cx).is_ready() {
return Poll::Ready(Err(Interrupted));
}
f.as_mut().poll(cx).map(Ok)
})
.await
}