//! Cooperative multitasking with async/await. //! //! An `async fn` compiles to a state machine implementing `Future`. Calling //! `poll` on it runs it until it either finishes (`Poll::Ready`) or has to //! wait (`Poll::Pending`) -- e.g. for a key press. Before returning Pending, //! the future hands the `Waker` from its `Context` to whoever will make //! progress possible (here: the keyboard interrupt). When that happens, //! `waker.wake()` tells the executor to poll the task again. //! //! "Cooperative": a task only gives up the CPU at an `.await`. A task that //! loops forever without awaiting blocks every other task. (Compare the //! preemptive threads in `thread.rs`.) use alloc::boxed::Box; use core::{ future::Future, pin::Pin, sync::atomic::{AtomicU64, Ordering}, task::{Context, Poll}, }; pub mod executor; pub mod keyboard; pub mod simple_executor; /// A task = a heap-allocated, pinned future with no output. /// `Pin` because async state machines may contain pointers into themselves, /// so they must never move in memory once polled. pub struct Task { id: TaskId, future: Pin>>, } impl Task { pub fn new(future: impl Future + 'static) -> Task { Task { id: TaskId::new(), future: Box::pin(future) } } fn poll(&mut self, context: &mut Context) -> Poll<()> { self.future.as_mut().poll(context) } } /// Unique id per task, so wakers can say *which* task to run again. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct TaskId(u64); impl TaskId { fn new() -> Self { static NEXT_ID: AtomicU64 = AtomicU64::new(0); TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) } } /// A future that returns Pending once (waking itself straight away), then /// Ready. `yield_now().await` lets other tasks run -- handy for demos/tests. pub async fn yield_now() { struct YieldNow(bool); impl Future for YieldNow { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { if self.0 { return Poll::Ready(()); } self.0 = true; cx.waker().wake_by_ref(); // "poll me again soon" Poll::Pending } } YieldNow(false).await }