From 281d75ced93cdee90ef66e7b396c83be0c4e0442 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Wed, 7 Feb 2024 16:41:05 +0100 Subject: [PATCH] First commit. --- .gitignore | 1 + Cargo.lock | 7 ++++ Cargo.toml | 8 ++++ src/main.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..8b2e396 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "async_tests" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f87309a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "async_tests" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ae08e27 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,110 @@ +//! Experiments in manually polling and yielding from Futures. +//! +//! The idea here is that Futures are a really convenient way to write state +//! machines that progressively do computations. However, the whole async +//! ecosystem so far seems to be built on the following assumptions: +//! +//! 1. async code is primarily meant for yielding on IO, not for doing +//! computations in a controlled iterative way. +//! 2. Polling async code should be done by "executors" rather than manually. +//! +//! Below is an example of using async outside of those assumptions. In +//! this case, we have async code that never does any IO, and is purely +//! doing computations. But it does those computations *progressively*, with +//! yield points to allow other code to run, and manual polling to move the +//! computation along as desired. + +use std::{ + future::Future, + pin::{pin, Pin}, + task::Context, + task::Poll, +}; + +fn main() { + // These are just examples of using the APIs further down in the file. + + // Polling an async function. + let mut b = pin!(foo(8)); + loop { + match poll(&mut b) { + Poll::Ready(v) => { + println!("{}", v); + break; + } + Poll::Pending => println!("Not yet!"), + } + } + + // Polling an async block that captures things. + let mut x = 5; + let y = &mut x; + let mut f = pin!(async move { + for _ in 0..10 { + *y += 1; + pause().await; + } + }); + loop { + match poll(&mut f) { + Poll::Ready(_) => { + println!("{}", x); + break; + } + Poll::Pending => println!("Not yet!"), + } + } +} + +async fn foo(a: i32) -> i32 { + let mut b = 26; + for _ in 0..10 { + b += a; + pause().await; + } + b +} + +//------------------------------------------------------------- + +/// If awaited, will yield control to the executor immediately. +pub fn pause() -> Pause { + Pause { first_time: true } +} + +/// A future that simply yields to the executor. +pub struct Pause { + first_time: bool, +} + +impl Future for Pause { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.first_time { + self.as_mut().first_time = false; + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +//------------------------------------------------------------- + +/// Polls a future once, without a waker. +pub fn poll(future: F) -> Poll { + use std::task::{RawWaker, RawWakerVTable, Waker}; + + // TODO: replace with `std::task::Waker::noop()` if/when it + // lands on stable Rust. + let noop_waker = { + const VTABLE: RawWakerVTable = RawWakerVTable::new(|_| RAW, |_| {}, |_| {}, |_| {}); + const RAW: RawWaker = RawWaker::new(std::ptr::null(), &VTABLE); + unsafe { Waker::from_raw(RAW) } + }; + + let mut context = Context::from_waker(&noop_waker); + pin!(future).poll(&mut context) +}