From 13ccd92e630e2d2a477b5062a995a0fb1a2b7a28 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 May 2022 05:45:14 +0200 Subject: [PATCH] Starting to implement async task codgen --- examples/async-task.rs | 210 +++++++++++++++++++++++++++ macros/Cargo.toml | 3 +- macros/src/codegen/software_tasks.rs | 8 +- 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 examples/async-task.rs diff --git a/examples/async-task.rs b/examples/async-task.rs new file mode 100644 index 0000000000..a91dfc6785 --- /dev/null +++ b/examples/async-task.rs @@ -0,0 +1,210 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll, Waker}; + +use cortex_m_semihosting::{debug, hprintln}; +use panic_semihosting as _; +use systick_monotonic::*; + +// NOTES: +// +// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// task can have a mutable reference stored. +// - Spawning an async task equates to it being polled at least once. +// - ... + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use crate::*; + + pub type AppInstant = as rtic::Monotonic>::Instant; + pub type AppDuration = as rtic::Monotonic>::Duration; + + #[shared] + struct Shared { + s: u32, + } + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + ( + Shared { s: 0 }, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + // debug::exit(debug::EXIT_SUCCESS); + loop { + // hprintln!("idle"); + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task(priority = 2)] + async fn task(cx: task::Context) { + hprintln!("delay long time").ok(); + + let fut = Delay::spawn(2500.millis()); + + hprintln!("we have just created the future").ok(); + fut.await; + hprintln!("long delay done").ok(); + + hprintln!("delay short time").ok(); + delay(500.millis()).await; + hprintln!("short delay done").ok(); + + hprintln!("test timeout").ok(); + let res = timeout(NeverEndingFuture {}, 1.secs()).await; + hprintln!("timeout done: {:?}", res).ok(); + + hprintln!("test timeout 2").ok(); + let res = timeout(delay(500.millis()), 1.secs()).await; + hprintln!("timeout done 2: {:?}", res).ok(); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(capacity = 12)] + fn delay_handler(_: delay_handler::Context, waker: Waker) { + waker.wake(); + } +} + +// Delay + +pub struct Delay { + until: crate::app::AppInstant, +} + +impl Delay { + pub fn spawn(duration: crate::app::AppDuration) -> Self { + let until = crate::app::monotonics::now() + duration; + + Delay { until } + } +} + +#[inline(always)] +pub fn delay(duration: crate::app::AppDuration) -> Delay { + Delay::spawn(duration) +} + +impl Future for Delay { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let s = self.as_mut(); + let now = crate::app::monotonics::now(); + + hprintln!(" poll Delay").ok(); + + if now >= s.until { + Poll::Ready(()) + } else { + let waker = cx.waker().clone(); + crate::app::delay_handler::spawn_after(s.until - now, waker).ok(); + + Poll::Pending + } + } +} + +//============= +// Timeout future + +#[derive(Copy, Clone, Debug)] +pub struct TimeoutError; + +pub struct Timeout { + future: F, + until: crate::app::AppInstant, + cancel_handle: Option, +} + +impl Timeout +where + F: Future, +{ + pub fn timeout(future: F, duration: crate::app::AppDuration) -> Self { + let until = crate::app::monotonics::now() + duration; + Self { + future, + until, + cancel_handle: None, + } + } +} + +#[inline(always)] +pub fn timeout(future: F, duration: crate::app::AppDuration) -> Timeout { + Timeout::timeout(future, duration) +} + +impl Future for Timeout +where + F: Future, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let now = crate::app::monotonics::now(); + + // SAFETY: We don't move the underlying pinned value. + let mut s = unsafe { self.get_unchecked_mut() }; + let future = unsafe { Pin::new_unchecked(&mut s.future) }; + + hprintln!(" poll Timeout").ok(); + + match future.poll(cx) { + Poll::Ready(r) => { + if let Some(ch) = s.cancel_handle.take() { + ch.cancel().ok(); + } + + Poll::Ready(Ok(r)) + } + Poll::Pending => { + if now >= s.until { + Poll::Ready(Err(TimeoutError)) + } else if s.cancel_handle.is_none() { + let waker = cx.waker().clone(); + let sh = crate::app::delay_handler::spawn_after(s.until - now, waker) + .expect("Internal RTIC bug, this should never fail"); + s.cancel_handle = Some(sh); + + Poll::Pending + } else { + Poll::Pending + } + } + } + } +} + +pub struct NeverEndingFuture {} + +impl Future for NeverEndingFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + // Never finish + hprintln!(" polling NeverEndingFuture").ok(); + Poll::Pending + } +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml index f1111e6ca1..a612221c1d 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -22,7 +22,8 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "1.0.2" +# rtic-syntax = "1.0.2" +rtic-syntax = { git = "https://github.com/rtic-rs/rtic-syntax.git", branch = "async-tasks" } #"1.0.0" [features] debugprint = [] diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 77559493bc..78f6c96128 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -27,7 +27,13 @@ pub fn codegen( let mut root = vec![]; let mut user_tasks = vec![]; - for (name, task) in &app.software_tasks { + // Async tasks + for (name, task) in app.software_tasks.iter().filter(|(_, task)| task.is_async) { + // todo + } + + // Non-async tasks + for (name, task) in app.software_tasks.iter().filter(|(_, task)| !task.is_async) { let inputs = &task.inputs; let (_, _, _, input_ty) = util::regroup_inputs(inputs);