Added interface for cancel/reschedule

Use wrapping add for marker

No need to store handle to queue

Remove unnecessary `SpawnHandle::new`

Fix test

Updated interface to follow proposal
This commit is contained in:
Emil Fresk 2021-03-11 19:12:02 +01:00
parent 4bdc187912
commit 1087f2ee64
6 changed files with 108 additions and 249 deletions

View file

@ -26,18 +26,29 @@ mod app {
let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000);
let _: Result<(), ()> = foo::spawn_after(Seconds(1_u32)); let a: Result<foo::MyMono::SpawnHandle, ()> = foo::spawn_after(Seconds(1_u32));
let _: Result<(), u32> = bar::spawn_after(Seconds(2_u32), 0); if let Ok(handle) = a {
let _: Result<(), (u32, u32)> = baz::spawn_after(Seconds(3_u32), 0, 1); let _: Result<foo::MyMono::SpawnHandle, ()> = handle.reschedule_after(Seconds(1_u32));
}
let b: Result<bar::MyMono::SpawnHandle, u32> = bar::spawn_after(Seconds(2_u32), 0);
if let Ok(handle) = b {
let _: Result<u32, ()> = handle.cancel();
}
let _: Result<baz::MyMono::SpawnHandle, (u32, u32)> =
baz::spawn_after(Seconds(3_u32), 0, 1);
(init::LateResources {}, init::Monotonics(mono)) (init::LateResources {}, init::Monotonics(mono))
} }
#[idle] #[idle]
fn idle(_: idle::Context) -> ! { fn idle(_: idle::Context) -> ! {
let _: Result<(), ()> = foo::spawn_at(MyMono::now() + Seconds(3_u32)); let _: Result<foo::MyMono::SpawnHandle, ()> = foo::spawn_at(MyMono::now() + Seconds(3_u32));
let _: Result<(), u32> = bar::spawn_at(MyMono::now() + Seconds(4_u32), 0); let _: Result<bar::MyMono::SpawnHandle, u32> =
let _: Result<(), (u32, u32)> = baz::spawn_at(MyMono::now() + Seconds(5_u32), 0, 1); bar::spawn_at(MyMono::now() + Seconds(4_u32), 0);
let _: Result<baz::MyMono::SpawnHandle, (u32, u32)> =
baz::spawn_at(MyMono::now() + Seconds(5_u32), 0, 1);
loop { loop {
cortex_m::asm::nop(); cortex_m::asm::nop();

View file

@ -264,15 +264,64 @@ pub fn codegen(
}; };
let user_imports = &app.user_imports; let user_imports = &app.user_imports;
let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident());
items.push(quote!( items.push(quote!(
/// Holds methods related to this monotonic /// Holds methods related to this monotonic
pub mod #m { pub mod #m {
#[allow(unused_imports)]
use #app_path::#tq_marker;
#[allow(unused_imports)]
use #app_path::#t;
#( #(
#[allow(unused_imports)] #[allow(unused_imports)]
#user_imports #user_imports
)* )*
pub struct SpawnHandle {
#[doc(hidden)]
marker: u32,
}
// TODO: remove
impl core::fmt::Debug for SpawnHandle
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let handle = unsafe { &#app_path::#tq as *const _ as u32 };
f.debug_struct("SpawnHandle")
.field("marker", &self.marker)
.field("handle", &handle)
.finish()
}
}
impl SpawnHandle {
pub fn cancel(self) -> Result<#ty, ()> {
// TODO: Actually cancel...
// &mut #app_path::#tq;
Err(())
}
#[inline]
pub fn reschedule_after<D>(self, duration: D) -> Result<Self, ()>
where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint,
D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>,
{
self.reschedule_at(#app_path::#m::now() + duration)
}
pub fn reschedule_at(self, instant: rtic::time::Instant<#app_path::#mono_type>) -> Result<Self, ()>
{
let _ = instant;
// TODO: Actually reschedule...
// &mut #app_path::#tq;
Err(())
}
}
#(#cfgs)* #(#cfgs)*
/// Spawns the task after a set duration relative to the current time /// Spawns the task after a set duration relative to the current time
/// ///
@ -281,7 +330,7 @@ pub fn codegen(
pub fn spawn_after<D>( pub fn spawn_after<D>(
duration: D duration: D
#(,#args)* #(,#args)*
) -> Result<(), #ty> ) -> Result<SpawnHandle, #ty>
where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint,
D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>, D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>,
{ {
@ -300,7 +349,7 @@ pub fn codegen(
pub fn spawn_at( pub fn spawn_at(
instant: rtic::time::Instant<#app_path::#mono_type> instant: rtic::time::Instant<#app_path::#mono_type>
#(,#args)* #(,#args)*
) -> Result<(), #ty> { ) -> Result<SpawnHandle, #ty> {
unsafe { unsafe {
let input = #tupled; let input = #tupled;
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
@ -314,13 +363,17 @@ pub fn codegen(
.as_mut_ptr() .as_mut_ptr()
.write(instant); .write(instant);
let nr = rtic::export::NotReady { rtic::export::interrupt::free(|_| {
instant, let marker = #tq_marker;
index, let nr = rtic::export::NotReady {
task: #app_path::#t::#name, instant,
}; index,
task: #app_path::#t::#name,
marker,
};
#tq_marker = #tq_marker.wrapping_add(1);
rtic::export::interrupt::free(|_|
if let Some(mono) = #app_path::#m_ident.as_mut() { if let Some(mono) = #app_path::#m_ident.as_mut() {
#app_path::#tq.enqueue_unchecked( #app_path::#tq.enqueue_unchecked(
nr, nr,
@ -331,9 +384,10 @@ pub fn codegen(
// We can only use the timer queue if `init` has returned, and it // We can only use the timer queue if `init` has returned, and it
// writes the `Some(monotonic)` we are accessing here. // writes the `Some(monotonic)` we are accessing here.
core::hint::unreachable_unchecked() core::hint::unreachable_unchecked()
}); }
Ok(()) Ok(SpawnHandle { marker })
})
} else { } else {
Err(input) Err(input)
} }

View file

@ -9,6 +9,15 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let mut items = vec![]; let mut items = vec![];
if !app.monotonics.is_empty() { if !app.monotonics.is_empty() {
// Generate the marker counter used to track for `cancel` and `reschedule`
let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident());
items.push(quote!(
// #[doc = #doc]
#[doc(hidden)]
#[allow(non_camel_case_types)]
static mut #tq_marker: u32 = 0;
));
let t = util::schedule_t_ident(); let t = util::schedule_t_ident();
// Enumeration of `schedule`-able tasks // Enumeration of `schedule`-able tasks
@ -32,7 +41,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#[doc(hidden)] #[doc(hidden)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum #t { pub enum #t {
#(#variants,)* #(#variants,)*
} }
)); ));

View file

@ -89,6 +89,11 @@ pub fn interrupt_ident() -> Ident {
Ident::new("interrupt", span) Ident::new("interrupt", span)
} }
pub fn timer_queue_marker_ident() -> Ident {
let span = Span::call_site();
Ident::new("TIMER_QUEUE_MARKER", span)
}
/// Whether `name` is an exception with configurable priority /// Whether `name` is an exception with configurable priority
pub fn is_exception(name: &Ident) -> bool { pub fn is_exception(name: &Ident) -> bool {
let s = name.to_string(); let s = name.to_string();

View file

@ -1,221 +0,0 @@
//! Data Watchpoint Trace (DWT) unit's CYCle CouNTer (CYCCNT)
use core::{
cmp::Ordering,
convert::{Infallible, TryInto},
fmt, ops,
};
use cortex_m::peripheral::DWT;
use crate::Fraction;
/// A measurement of the CYCCNT. Opaque and useful only with `Duration`
///
/// This data type is only available on ARMv7-M
///
/// # Correctness
///
/// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively
/// makes it "wrap around" and creates an incorrect value. This is also true if the operation is
/// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks.
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Instant {
inner: i32,
}
impl Instant {
/// Returns an instant corresponding to "now"
///
/// *HEADS UP* this function can, and will, return nonsensical values if called within `init`.
/// Only use it in `idle` and tasks. In `init`, use the `init::Context.start` field, or the
/// `CYCCNT::zero` function, instead of this function
pub fn now() -> Self {
Instant {
inner: DWT::get_cycle_count() as i32,
}
}
/// Returns the amount of time elapsed since this instant was created.
pub fn elapsed(&self) -> Duration {
let diff = Instant::now().inner.wrapping_sub(self.inner);
assert!(diff >= 0, "instant now is earlier than self");
Duration { inner: diff as u32 }
}
/// Returns the amount of time elapsed from another instant to this one.
pub fn duration_since(&self, earlier: Instant) -> Duration {
let diff = self.inner.wrapping_sub(earlier.inner);
assert!(diff >= 0, "second instant is later than self");
Duration { inner: diff as u32 }
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Instant")
.field(&(self.inner as u32))
.finish()
}
}
impl ops::AddAssign<Duration> for Instant {
fn add_assign(&mut self, dur: Duration) {
// NOTE this is a debug assertion because there's no foolproof way to detect a wrap around;
// the user may write `(instant + dur) + dur` where `dur` is `(1<<31)-1` ticks.
debug_assert!(dur.inner < (1 << 31));
self.inner = self.inner.wrapping_add(dur.inner as i32);
}
}
impl ops::Add<Duration> for Instant {
type Output = Self;
fn add(mut self, dur: Duration) -> Self {
self += dur;
self
}
}
impl ops::SubAssign<Duration> for Instant {
fn sub_assign(&mut self, dur: Duration) {
// NOTE see the NOTE in `<Instant as AddAssign<Duration>>::add_assign`
debug_assert!(dur.inner < (1 << 31));
self.inner = self.inner.wrapping_sub(dur.inner as i32);
}
}
impl ops::Sub<Duration> for Instant {
type Output = Self;
fn sub(mut self, dur: Duration) -> Self {
self -= dur;
self
}
}
impl ops::Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
}
impl Ord for Instant {
fn cmp(&self, rhs: &Self) -> Ordering {
self.inner.wrapping_sub(rhs.inner).cmp(&0)
}
}
impl PartialOrd for Instant {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
/// A `Duration` type to represent a span of time.
///
/// This data type is only available on ARMv7-M
///
/// # Correctness
///
/// This type is *not* appropriate for representing time spans in the order of, or larger than,
/// seconds because it can hold a maximum of `(1 << 31)` "ticks" where each tick is the inverse of
/// the CPU frequency, which usually is dozens of MHz.
#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Duration {
inner: u32,
}
impl Duration {
/// Creates a new `Duration` from the specified number of clock cycles
pub fn from_cycles(cycles: u32) -> Self {
Duration { inner: cycles }
}
/// Returns the total number of clock cycles contained by this `Duration`
pub fn as_cycles(&self) -> u32 {
self.inner
}
}
impl TryInto<u32> for Duration {
type Error = Infallible;
fn try_into(self) -> Result<u32, Infallible> {
Ok(self.as_cycles())
}
}
impl ops::AddAssign for Duration {
fn add_assign(&mut self, dur: Duration) {
self.inner += dur.inner;
}
}
impl ops::Add<Duration> for Duration {
type Output = Self;
fn add(self, other: Self) -> Self {
Duration {
inner: self.inner + other.inner,
}
}
}
impl ops::SubAssign for Duration {
fn sub_assign(&mut self, rhs: Duration) {
self.inner -= rhs.inner;
}
}
impl ops::Sub<Duration> for Duration {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Duration {
inner: self.inner - rhs.inner,
}
}
}
/// Adds the `cycles` method to the `u32` type
///
/// This trait is only available on ARMv7-M
pub trait U32Ext {
/// Converts the `u32` value into clock cycles
fn cycles(self) -> Duration;
}
impl U32Ext for u32 {
fn cycles(self) -> Duration {
Duration { inner: self }
}
}
/// Implementation of the `Monotonic` trait based on CYCle CouNTer
pub struct CYCCNT;
impl crate::Monotonic for CYCCNT {
type Instant = Instant;
fn ratio() -> Fraction {
Fraction {
numerator: 1,
denominator: 1,
}
}
unsafe fn reset() {
(0xE0001004 as *mut u32).write_volatile(0)
}
fn now() -> Instant {
Instant::now()
}
fn zero() -> Instant {
Instant { inner: 0 }
}
}

View file

@ -5,6 +5,15 @@ use crate::{
use core::cmp::Ordering; use core::cmp::Ordering;
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
#[inline]
fn unwrapper<T, E>(val: Result<T, E>) -> T {
if let Ok(v) = val {
v
} else {
unreachable!("Your monotonic is not infallible")
}
}
pub struct TimerQueue<Mono, Task, N>(pub BinaryHeap<NotReady<Mono, Task>, N, Min>) pub struct TimerQueue<Mono, Task, N>(pub BinaryHeap<NotReady<Mono, Task>, N, Min>)
where where
Mono: Monotonic, Mono: Monotonic,
@ -66,15 +75,6 @@ where
self.0.is_empty() self.0.is_empty()
} }
#[inline]
fn unwrapper<T, E>(val: Result<T, E>) -> T {
if let Ok(v) = val {
v
} else {
unreachable!("Your monotonic is not infallible")
}
}
/// Dequeue a task from the TimerQueue /// Dequeue a task from the TimerQueue
#[inline] #[inline]
pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)>
@ -84,7 +84,7 @@ where
mono.clear_compare_flag(); mono.clear_compare_flag();
if let Some(instant) = self.0.peek().map(|p| p.instant) { if let Some(instant) = self.0.peek().map(|p| p.instant) {
if instant <= Self::unwrapper(Clock::try_now(mono)) { if instant <= unwrapper(Clock::try_now(mono)) {
// task became ready // task became ready
let nr = unsafe { self.0.pop_unchecked() }; let nr = unsafe { self.0.pop_unchecked() };
@ -97,7 +97,7 @@ where
// dequeue. If the monotonic is fast enough it can happen that from the // dequeue. If the monotonic is fast enough it can happen that from the
// read of now to the set of the compare, the time can overflow. This is to // read of now to the set of the compare, the time can overflow. This is to
// guard against this. // guard against this.
if instant <= Self::unwrapper(Clock::try_now(mono)) { if instant <= unwrapper(Clock::try_now(mono)) {
let nr = unsafe { self.0.pop_unchecked() }; let nr = unsafe { self.0.pop_unchecked() };
Some((nr.task, nr.index)) Some((nr.task, nr.index))
@ -125,6 +125,7 @@ where
pub index: u8, pub index: u8,
pub instant: Instant<Mono>, pub instant: Instant<Mono>,
pub task: Task, pub task: Task,
pub marker: u32,
} }
impl<Mono, Task> Eq for NotReady<Mono, Task> impl<Mono, Task> Eq for NotReady<Mono, Task>