use crate::syntax::ast::App; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use crate::{analyze::Analysis, codegen::util}; /// Generates monotonic module dispatchers pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { let mut monotonic_parts: Vec<_> = Vec::new(); let tq_marker = util::timer_queue_marker_ident(); for (_, monotonic) in &app.monotonics { // let instants = util::monotonic_instants_ident(name, &monotonic.ident); let monotonic_name = monotonic.ident.to_string(); let tq = util::tq_ident(&monotonic_name); let m = &monotonic.ident; let m_ident = util::monotonic_ident(&monotonic_name); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); let name_str = &m.to_string(); let ident = util::monotonic_ident(name_str); let doc = &format!( "This module holds the static implementation for `{}::now()`", name_str ); let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { ( quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), quote!(rtic::export::SCB::set_pendst()), ) } else { let rt_err = util::rt_err_ident(); ( quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), ) }; let default_monotonic = if monotonic.args.default { quote!( #[doc(inline)] pub use #m::now; #[doc(inline)] pub use #m::delay; #[doc(inline)] pub use #m::delay_until; #[doc(inline)] pub use #m::timeout_at; #[doc(inline)] pub use #m::timeout_after; ) } else { quote!() }; monotonic_parts.push(quote! { #default_monotonic #[doc = #doc] #[allow(non_snake_case)] pub mod #m { /// Read the current time from this monotonic pub fn now() -> ::Instant { rtic::export::interrupt::free(|_| { use rtic::Monotonic as _; if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { m.now() } else { ::zero() } }) } /// Delay #[inline(always)] #[allow(non_snake_case)] pub fn delay(duration: ::Duration) -> DelayFuture { let until = now() + duration; DelayFuture { until, waker_storage: None } } /// Delay until a specific time #[inline(always)] #[allow(non_snake_case)] pub fn delay_until(instant: ::Instant) -> DelayFuture { let until = instant; DelayFuture { until, waker_storage: None } } /// Delay future. #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct DelayFuture { until: ::Instant, waker_storage: Option>>, } impl Drop for DelayFuture { fn drop(&mut self) { if let Some(waker_storage) = &mut self.waker_storage { rtic::export::interrupt::free(|_| unsafe { let tq = &mut *super::super::#tq.get_mut(); tq.cancel_waker_marker(waker_storage.val.marker); }); } } } impl core::future::Future for DelayFuture { type Output = (); fn poll( mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_> ) -> core::task::Poll { let mut s = self.as_mut(); let now = now(); let until = s.until; let is_ws_none = s.waker_storage.is_none(); if now >= until { return core::task::Poll::Ready(()); } else if is_ws_none { rtic::export::interrupt::free(|_| unsafe { let marker = super::super::#tq_marker.get().read(); super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { waker: cx.waker().clone(), instant: until, marker, })); let tq = &mut *super::super::#tq.get_mut(); tq.enqueue_waker( core::mem::transmute(nr), // Transmute the reference to static || #enable_interrupt, || #pend, (&mut *super::super::#m_ident.get_mut()).as_mut()); }); } core::task::Poll::Pending } } /// Timeout future. #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct TimeoutFuture { future: F, until: ::Instant, waker_storage: Option>>, } impl Drop for TimeoutFuture { fn drop(&mut self) { if let Some(waker_storage) = &mut self.waker_storage { rtic::export::interrupt::free(|_| unsafe { let tq = &mut *super::super::#tq.get_mut(); tq.cancel_waker_marker(waker_storage.val.marker); }); } } } /// Timeout after #[allow(non_snake_case)] #[inline(always)] pub fn timeout_after( future: F, duration: ::Duration ) -> TimeoutFuture { let until = now() + duration; TimeoutFuture { future, until, waker_storage: None, } } /// Timeout at #[allow(non_snake_case)] #[inline(always)] pub fn timeout_at( future: F, instant: ::Instant ) -> TimeoutFuture { TimeoutFuture { future, until: instant, waker_storage: None, } } impl core::future::Future for TimeoutFuture where F: core::future::Future, { type Output = Result; fn poll( self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_> ) -> core::task::Poll { // SAFETY: We don't move the underlying pinned value. let mut s = unsafe { self.get_unchecked_mut() }; let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; let now = now(); let until = s.until; let is_ws_none = s.waker_storage.is_none(); match future.poll(cx) { core::task::Poll::Ready(r) => { if let Some(waker_storage) = &mut s.waker_storage { rtic::export::interrupt::free(|_| unsafe { let tq = &mut *super::super::#tq.get_mut(); tq.cancel_waker_marker(waker_storage.val.marker); }); } return core::task::Poll::Ready(Ok(r)); } core::task::Poll::Pending => { if now >= until { // Timeout return core::task::Poll::Ready(Err(super::TimeoutError)); } else if is_ws_none { rtic::export::interrupt::free(|_| unsafe { let marker = super::super::#tq_marker.get().read(); super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { waker: cx.waker().clone(), instant: until, marker, })); let tq = &mut *super::super::#tq.get_mut(); tq.enqueue_waker( core::mem::transmute(nr), // Transmute the reference to static || #enable_interrupt, || #pend, (&mut *super::super::#m_ident.get_mut()).as_mut()); }); } } } core::task::Poll::Pending } } } }); } if monotonic_parts.is_empty() { quote!() } else { quote!( pub use rtic::Monotonic as _; /// Holds static methods for each monotonic. pub mod monotonics { /// A timeout error. #[derive(Debug)] pub struct TimeoutError; #(#monotonic_parts)* } ) } }