diff --git a/ci/expected/async-delay.run b/ci/expected/async-delay.run new file mode 100644 index 0000000000..61852abfd9 --- /dev/null +++ b/ci/expected/async-delay.run @@ -0,0 +1,7 @@ +init +hello from bar +hello from baz +hello from foo +bye from foo +bye from bar +bye from baz diff --git a/ci/expected/async-infinite-loop.run b/ci/expected/async-infinite-loop.run new file mode 100644 index 0000000000..f9fd4e494c --- /dev/null +++ b/ci/expected/async-infinite-loop.run @@ -0,0 +1,6 @@ +init +hello from async 0 +hello from async 1 +hello from async 2 +hello from async 3 +hello from async 4 diff --git a/ci/expected/async-task-multiple-prios.run b/ci/expected/async-task-multiple-prios.run new file mode 100644 index 0000000000..9b0f53365b --- /dev/null +++ b/ci/expected/async-task-multiple-prios.run @@ -0,0 +1,5 @@ +init +hello from normal 2 +hello from async 2 +hello from normal 1 +hello from async 1 diff --git a/ci/expected/async-task.run b/ci/expected/async-task.run new file mode 100644 index 0000000000..f7ce3a6065 --- /dev/null +++ b/ci/expected/async-task.run @@ -0,0 +1,3 @@ +init +hello from normal +hello from async diff --git a/ci/expected/async-timeout.run b/ci/expected/async-timeout.run new file mode 100644 index 0000000000..a8074230ee --- /dev/null +++ b/ci/expected/async-timeout.run @@ -0,0 +1,5 @@ +init +hello from bar +hello from foo +foo no timeout +bar timeout diff --git a/ci/expected/periodic-at.run b/ci/expected/periodic-at.run index 54020f9e95..bf5bb0631b 100644 --- a/ci/expected/periodic-at.run +++ b/ci/expected/periodic-at.run @@ -1,4 +1,4 @@ foo Instant { ticks: 0 } -foo Instant { ticks: 100 } -foo Instant { ticks: 200 } -foo Instant { ticks: 300 } +foo Instant { ticks: 10 } +foo Instant { ticks: 20 } +foo Instant { ticks: 30 } diff --git a/ci/expected/periodic-at2.run b/ci/expected/periodic-at2.run index 47adbef486..6e56421a30 100644 --- a/ci/expected/periodic-at2.run +++ b/ci/expected/periodic-at2.run @@ -1,7 +1,7 @@ foo Instant { ticks: 0 } bar Instant { ticks: 10 } -foo Instant { ticks: 110 } -bar Instant { ticks: 120 } -foo Instant { ticks: 220 } -bar Instant { ticks: 230 } -foo Instant { ticks: 330 } +foo Instant { ticks: 30 } +bar Instant { ticks: 40 } +foo Instant { ticks: 60 } +bar Instant { ticks: 70 } +foo Instant { ticks: 90 } diff --git a/ci/expected/pool.run b/ci/expected/pool.run index 81f79d41e9..00d1188d8a 100644 --- a/ci/expected/pool.run +++ b/ci/expected/pool.run @@ -1,2 +1,2 @@ -bar(0x20000088) -foo(0x2000010c) +bar(0x20000084) +foo(0x20000108) diff --git a/examples/async-delay.rs b/examples/async-delay.rs new file mode 100644 index 0000000000..7802bda4d4 --- /dev/null +++ b/examples/async-delay.rs @@ -0,0 +1,67 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[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(); + + foo::spawn().ok(); + bar::spawn().ok(); + baz::spawn().ok(); + + ( + Shared {}, + 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] + async fn foo(_cx: foo::Context) { + hprintln!("hello from foo").ok(); + monotonics::delay(100.millis()).await; + hprintln!("bye from foo").ok(); + } + + #[task] + async fn bar(_cx: bar::Context) { + hprintln!("hello from bar").ok(); + monotonics::delay(200.millis()).await; + hprintln!("bye from bar").ok(); + } + + #[task] + async fn baz(_cx: baz::Context) { + hprintln!("hello from baz").ok(); + monotonics::delay(300.millis()).await; + hprintln!("bye from baz").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } +} diff --git a/examples/async-infinite-loop.rs b/examples/async-infinite-loop.rs new file mode 100644 index 0000000000..7615818d3c --- /dev/null +++ b/examples/async-infinite-loop.rs @@ -0,0 +1,57 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[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(); + + foo::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + // Infinite loops are not allowed in RTIC, however in async tasks they are - if there is an + // await inside the loop. + #[task] + async fn foo(_cx: foo::Context) { + let mut i = 0; + loop { + if i == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + + hprintln!("hello from async {}", i).ok(); + monotonics::delay(100.millis()).await; // This makes it okey! + + i += 1; + } + } +} diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs new file mode 100644 index 0000000000..3e197987a2 --- /dev/null +++ b/examples/async-task-multiple-prios.rs @@ -0,0 +1,76 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// 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 once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0, UART0, UART1], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared { + a: u32, + b: 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(); + + normal_task::spawn().ok(); + async_task::spawn().ok(); + normal_task2::spawn().ok(); + async_task2::spawn().ok(); + + ( + Shared { a: 0, b: 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 = 1, shared = [a, b])] + fn normal_task(_cx: normal_task::Context) { + hprintln!("hello from normal 1").ok(); + } + + #[task(priority = 1, shared = [a, b])] + async fn async_task(_cx: async_task::Context) { + hprintln!("hello from async 1").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2, shared = [a, b])] + fn normal_task2(_cx: normal_task2::Context) { + hprintln!("hello from normal 2").ok(); + } + + #[task(priority = 2, shared = [a, b])] + async fn async_task2(_cx: async_task2::Context) { + hprintln!("hello from async 2").ok(); + } +} diff --git a/examples/async-task.rs b/examples/async-task.rs new file mode 100644 index 0000000000..4d25ec4401 --- /dev/null +++ b/examples/async-task.rs @@ -0,0 +1,61 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// 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 once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[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(); + + normal_task::spawn().ok(); + async_task::spawn().ok(); + + ( + Shared {}, + 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] + fn normal_task(_cx: normal_task::Context) { + hprintln!("hello from normal").ok(); + } + + #[task] + async fn async_task(_cx: async_task::Context) { + hprintln!("hello from async").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } +} diff --git a/examples/async-timeout.rs b/examples/async-timeout.rs new file mode 100644 index 0000000000..3f68df744d --- /dev/null +++ b/examples/async-timeout.rs @@ -0,0 +1,87 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// 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 once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll}, + }; + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[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(); + + foo::spawn().ok(); + bar::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task] + async fn foo(_cx: foo::Context) { + hprintln!("hello from foo").ok(); + + // This will not timeout + match monotonics::timeout_after(monotonics::delay(100.millis()), 200.millis()).await { + Ok(_) => hprintln!("foo no timeout").ok(), + Err(_) => hprintln!("foo timeout").ok(), + }; + } + + #[task] + async fn bar(_cx: bar::Context) { + hprintln!("hello from bar").ok(); + + // This will timeout + match monotonics::timeout_after(NeverEndingFuture {}, 300.millis()).await { + Ok(_) => hprintln!("bar no timeout").ok(), + Err(_) => hprintln!("bar timeout").ok(), + }; + + debug::exit(debug::EXIT_SUCCESS); + } + + pub struct NeverEndingFuture {} + + impl Future for NeverEndingFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + // Never finish + Poll::Pending + } + } +} diff --git a/examples/binds.rs b/examples/binds.rs index 1b0c8c5beb..56565cbec9 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -24,22 +23,21 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting cortex_m::asm::nop(); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } @@ -51,6 +49,7 @@ mod app { "foo called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); } } diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index 36c496b71a..a38a9c4eae 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init"); + hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -43,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) let spawn_handle = baz::spawn_after(2.secs()).unwrap(); @@ -52,7 +51,7 @@ mod app { #[task] fn bar(_: bar::Context, baz_handle: baz::SpawnHandle, do_reschedule: bool) { - hprintln!("bar"); + hprintln!("bar").ok(); if do_reschedule { // Reschedule baz 2 seconds from now, instead of the original 1 second @@ -68,7 +67,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/capacity.rs b/examples/capacity.rs index 550829be32..a617269869 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -38,12 +37,12 @@ mod app { #[task(capacity = 4)] fn foo(_: foo::Context, x: u32) { - hprintln!("foo({})", x); + hprintln!("foo({})", x).unwrap(); } #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index 17f31f4ebb..f41866db47 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -82,19 +81,6 @@ mod app { // .. } - // The whole task should disappear, - // currently still present in the Tasks enum - #[cfg(never)] - #[task(binds = UART1, shared = [count])] - fn foo3(mut _cx: foo3::Context) { - #[cfg(debug_assertions)] - { - _cx.shared.count.lock(|count| *count += 10); - - log::spawn(_cx.shared.count.lock(|count| *count)).unwrap(); - } - } - #[cfg(debug_assertions)] #[task(capacity = 2)] fn log(_: log::Context, n: u32) { @@ -102,6 +88,7 @@ mod app { "foo has been called {} time{}", n, if n == 1 { "" } else { "s" } - ); + ) + .ok(); } } diff --git a/examples/common.rs b/examples/common.rs index 74ee8db2c8..1fe671e61a 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -74,7 +73,7 @@ mod app { // This task is only spawned once in `init`, hence this task will run // only once - hprintln!("foo"); + hprintln!("foo").ok(); } // Software task, also not bound to a hardware interrupt @@ -82,7 +81,7 @@ mod app { // The resources `s1` and `s2` are shared between all other tasks. #[task(shared = [s1, s2], local = [l2])] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").ok(); // Run `bar` once per second bar::spawn_after(1.secs()).unwrap(); @@ -98,6 +97,6 @@ mod app { // Note that RTIC does NOT clear the interrupt flag, this is up to the // user - hprintln!("UART0 interrupt!"); + hprintln!("UART0 interrupt!").ok(); } } diff --git a/examples/complex.rs b/examples/complex.rs index 73df025d2f..e5cf6dbea3 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -26,7 +25,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); ( Shared { @@ -41,31 +40,31 @@ mod app { #[idle(shared = [s2, s3])] fn idle(mut cx: idle::Context) -> ! { - hprintln!("idle p0 started"); + hprintln!("idle p0 started").ok(); rtic::pend(Interrupt::GPIOC); cx.shared.s3.lock(|s| { - hprintln!("idle enter lock s3 {}", s); - hprintln!("idle pend t0"); + hprintln!("idle enter lock s3 {}", s).ok(); + hprintln!("idle pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 3 - hprintln!("idle pend t1"); + hprintln!("idle pend t1").ok(); rtic::pend(Interrupt::GPIOB); // t1 p3, with shared ceiling 3 - hprintln!("idle pend t2"); + hprintln!("idle pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s3 {}", s); + hprintln!("idle still in lock s3 {}", s).ok(); }); - hprintln!("\nback in idle"); + hprintln!("\nback in idle").ok(); cx.shared.s2.lock(|s| { - hprintln!("enter lock s2 {}", s); - hprintln!("idle pend t0"); + hprintln!("enter lock s2 {}", s).ok(); + hprintln!("idle pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("idle pend t1"); + hprintln!("idle pend t1").ok(); rtic::pend(Interrupt::GPIOB); // t1 p3, no sharing - hprintln!("idle pend t2"); + hprintln!("idle pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s2 {}", s); + hprintln!("idle still in lock s2 {}", s).ok(); }); - hprintln!("\nidle exit"); + hprintln!("\nidle exit").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -83,8 +82,9 @@ mod app { "t0 p2 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); - hprintln!("t0 p2 exit"); + ) + .ok(); + hprintln!("t0 p2 exit").ok(); } #[task(binds = GPIOB, priority = 3, local = [times: u32 = 0], shared = [s3, s4])] @@ -96,18 +96,19 @@ mod app { "t1 p3 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .ok(); cx.shared.s4.lock(|s| { - hprintln!("t1 enter lock s4 {}", s); - hprintln!("t1 pend t0"); + hprintln!("t1 enter lock s4 {}", s).ok(); + hprintln!("t1 pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("t1 pend t2"); + hprintln!("t1 pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("t1 still in lock s4 {}", s); + hprintln!("t1 still in lock s4 {}", s).ok(); }); - hprintln!("t1 p3 exit"); + hprintln!("t1 p3 exit").ok(); } #[task(binds = GPIOC, priority = 4, local = [times: u32 = 0], shared = [s4])] @@ -119,12 +120,13 @@ mod app { "t2 p4 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); cx.shared.s4.lock(|s| { - hprintln!("enter lock s4 {}", s); + hprintln!("enter lock s4 {}", s).ok(); *s += 1; }); - hprintln!("t3 p4 exit"); + hprintln!("t3 p4 exit").ok(); } } diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs index cb6214960f..52d354bc9a 100644 --- a/examples/declared_locals.rs +++ b/examples/declared_locals.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/destructure.rs b/examples/destructure.rs index 70b0dd7e6f..6019c225cc 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -43,7 +42,7 @@ mod app { let b = cx.shared.b; let c = cx.shared.c; - hprintln!("foo: a = {}, b = {}, c = {}", a, b, c); + hprintln!("foo: a = {}, b = {}, c = {}", a, b, c).unwrap(); } // De-structure-ing syntax @@ -51,6 +50,6 @@ mod app { fn bar(cx: bar::Context) { let bar::SharedResources { a, b, c } = cx.shared; - hprintln!("bar: a = {}, b = {}, c = {}", a, b, c); + hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index bfc85cfc82..4dc6633c5d 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -11,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the interrupt bound task `foo`. fn foo(_: app::foo::Context) { - hprintln!("foo called"); + hprintln!("foo called").ok(); } #[rtic::app(device = lm3s6965)] @@ -30,22 +29,21 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { cortex_m::asm::nop(); - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 446d31a77b..7f9b5a5f9b 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -11,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the spawnable task `foo`. fn foo(_c: app::foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y); + hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/generics.rs b/examples/generics.rs index bc4959fb7b..72b861ba91 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -33,22 +32,19 @@ mod app { #[task(binds = UART0, shared = [shared], local = [state: u32 = 0])] fn uart0(c: uart0::Context) { - hprintln!("UART0(STATE = {})", *c.local.state); + hprintln!("UART0(STATE = {})", *c.local.state).unwrap(); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); rtic::pend(Interrupt::UART1); - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting - cortex_m::asm::nop(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(binds = UART1, priority = 2, shared = [shared], local = [state: u32 = 0])] fn uart1(c: uart1::Context) { - hprintln!("UART1(STATE = {})", *c.local.state); + hprintln!("UART1(STATE = {})", *c.local.state).unwrap(); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -65,5 +61,5 @@ fn advance(state: &mut u32, mut shared: impl Mutex) { (old, *shared) }); - hprintln!("shared: {} -> {}", old, new); + hprintln!("shared: {} -> {}", old, new).unwrap(); } diff --git a/examples/hardware.rs b/examples/hardware.rs index a7fdb47a37..60632247fb 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -25,7 +24,7 @@ mod app { // `init` returns because interrupts are disabled rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } @@ -34,15 +33,14 @@ mod app { fn idle(_: idle::Context) -> ! { // interrupts are enabled again; the `UART0` handler runs at this point - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting cortex_m::asm::nop(); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } @@ -55,6 +53,7 @@ mod app { "UART0 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); } } diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index 5e52620d45..4a8a8dee2b 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit @@ -34,7 +33,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle"); + hprintln!("idle").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/idle.rs b/examples/idle.rs index ccec9bf273..55d6b15352 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } @@ -30,7 +29,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle"); + hprintln!("idle").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/init.rs b/examples/init.rs index afd3b98ce9..b8a5bc5b98 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -33,7 +32,7 @@ mod app { // to indicate that this is a critical seciton let _cs_token: bare_metal::CriticalSection = cx.cs; - hprintln!("init"); + hprintln!("init").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/locals.rs b/examples/locals.rs index 9e112be4b3..aa5d0fee30 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -2,8 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -18,11 +16,8 @@ mod app { #[local] struct Local { - /// Local foo local_to_foo: i64, - /// Local bar local_to_bar: i64, - /// Local idle local_to_idle: i64, } @@ -50,7 +45,7 @@ mod app { let local_to_idle = cx.local.local_to_idle; *local_to_idle += 1; - hprintln!("idle: local_to_idle = {}", local_to_idle); + hprintln!("idle: local_to_idle = {}", local_to_idle).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -74,7 +69,7 @@ mod app { // error: no `local_to_bar` field in `foo::LocalResources` // cx.local.local_to_bar += 1; - hprintln!("foo: local_to_foo = {}", local_to_foo); + hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); } // `local_to_bar` can only be accessed from this context @@ -86,6 +81,6 @@ mod app { // error: no `local_to_foo` field in `bar::LocalResources` // cx.local.local_to_foo += 1; - hprintln!("bar: local_to_bar = {}", local_to_bar); + hprintln!("bar: local_to_bar = {}", local_to_bar).unwrap(); } } diff --git a/examples/lock-free.rs b/examples/lock-free.rs index 6e5faadbf2..ea6ff1bf37 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -34,7 +33,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" foo = {}", counter); + hprintln!(" foo = {}", counter).unwrap(); } #[task(shared = [counter])] // <- same priority @@ -43,7 +42,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" bar = {}", counter); + hprintln!(" bar = {}", counter).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/lock.rs b/examples/lock.rs index 5b3e0bcc3c..f1a16968ce 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -30,7 +29,7 @@ mod app { // when omitted priority is assumed to be `1` #[task(shared = [shared])] fn foo(mut c: foo::Context) { - hprintln!("A"); + hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data c.shared.shared.lock(|shared| { @@ -40,7 +39,7 @@ mod app { // bar will *not* run right now due to the critical section bar::spawn().unwrap(); - hprintln!("B - shared = {}", *shared); + hprintln!("B - shared = {}", *shared).unwrap(); // baz does not contend for `shared` so it's allowed to run now baz::spawn().unwrap(); @@ -48,7 +47,7 @@ mod app { // critical section is over: bar can now start - hprintln!("E"); + hprintln!("E").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } @@ -62,11 +61,11 @@ mod app { *shared }); - hprintln!("D - shared = {}", shared); + hprintln!("D - shared = {}", shared).unwrap(); } #[task(priority = 3)] fn baz(_: baz::Context) { - hprintln!("C"); + hprintln!("C").unwrap(); } } diff --git a/examples/message.rs b/examples/message.rs index 8a6a12d5f4..76c5675aaa 100644 --- a/examples/message.rs +++ b/examples/message.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -27,7 +26,7 @@ mod app { #[task(local = [count: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); bar::spawn(*cx.local.count).unwrap(); *cx.local.count += 1; @@ -35,14 +34,14 @@ mod app { #[task] fn bar(_: bar::Context, x: u32) { - hprintln!("bar({})", x); + hprintln!("bar({})", x).unwrap(); baz::spawn(x + 1, x + 2).unwrap(); } #[task] fn baz(_: baz::Context, x: u32, y: u32) { - hprintln!("baz({}, {})", x, y); + hprintln!("baz({}, {})", x, y).unwrap(); if x + y > 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/message_passing.rs b/examples/message_passing.rs index 9550a5010a..ffa9537127 100644 --- a/examples/message_passing.rs +++ b/examples/message_passing.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -30,7 +29,7 @@ mod app { #[task(capacity = 3)] fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y); + hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/multilock.rs b/examples/multilock.rs index c7085cd51c..d99bae695e 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -49,7 +48,7 @@ mod app { *s2 += 1; *s3 += 1; - hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3); + hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3).unwrap(); }); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 68af04a6c6..aa79ad5626 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -2,16 +2,13 @@ // #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] use core::marker::PhantomData; use panic_semihosting as _; -/// Not sync pub struct NotSync { - /// Phantom action _0: PhantomData<*const ()>, } @@ -25,7 +22,6 @@ mod app { #[shared] struct Shared { - /// This resource is not Sync shared: NotSync, } diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index b32827abf2..8b0a77ef8c 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -31,13 +30,13 @@ mod app { #[task(shared = [&key])] fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; - hprintln!("foo(key = {:#x})", key); + hprintln!("foo(key = {:#x})", key).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2, shared = [&key])] fn bar(cx: bar::Context) { - hprintln!("bar(key = {:#x})", cx.shared.key); + hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); } } diff --git a/examples/periodic-at.rs b/examples/periodic-at.rs index ad8a5496f2..ca68ed5eb9 100644 --- a/examples/periodic-at.rs +++ b/examples/periodic-at.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -36,15 +35,15 @@ mod app { #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant); + hprintln!("foo {:?}", instant).ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - // Periodic ever 1 seconds - let next_instant = instant + 1.secs(); + // Periodic every 100 milliseconds + let next_instant = instant + 100.millis(); foo::spawn_at(next_instant, next_instant).unwrap(); } } diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.rs index 4719bdb7e4..ec9adcc50c 100644 --- a/examples/periodic-at2.rs +++ b/examples/periodic-at2.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mut mono = Systick::new(systick, 12_000_000); - foo::spawn_after(1.secs(), mono.now()).unwrap(); + foo::spawn_after(200.millis(), mono.now()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } @@ -37,7 +36,7 @@ mod app { // Using the explicit type of the timer implementation #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant); + hprintln!("foo {:?}", instant).ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { @@ -53,10 +52,10 @@ mod app { // This remains agnostic to the timer implementation #[task(local = [cnt: u32 = 0])] fn bar(_cx: bar::Context, instant: ::Instant) { - hprintln!("bar {:?}", instant); + hprintln!("bar {:?}", instant).ok(); - // Spawn a new message with 1s offset to spawned time - let next_instant = instant + 1.secs(); + // Spawn a new message with 200ms offset to spawned time + let next_instant = instant + 200.millis(); foo::spawn_at(next_instant, next_instant).unwrap(); } } diff --git a/examples/periodic.rs b/examples/periodic.rs index 13ca7c852c..2f9e8e6a64 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,21 +28,21 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - foo::spawn_after(1.secs()).unwrap(); + foo::spawn_after(100.millis()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - // Periodic ever 1 seconds - foo::spawn_after(1.secs()).unwrap(); + // Periodic every 100ms + foo::spawn_after(100.millis()).unwrap(); } } diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index cc9b9a11ce..d542c0e64d 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -1,7 +1,5 @@ -//! examples/peripherals-taken.rs -#![deny(warnings)] #![deny(unsafe_code)] -#![deny(missing_docs)] +#![deny(warnings)] #![no_main] #![no_std] diff --git a/examples/pool.rs b/examples/pool.rs index ab76370e71..d59bd91607 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -2,8 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -// pool!() generates a struct without docs -//#![deny(missing_docs)] #![no_main] #![no_std] @@ -58,7 +56,7 @@ mod app { #[task] fn foo(_: foo::Context, x: Box

) { - hprintln!("foo({:?})", x.as_ptr()); + hprintln!("foo({:?})", x.as_ptr()).unwrap(); // explicitly return the block to the pool drop(x); @@ -68,7 +66,7 @@ mod app { #[task(priority = 2)] fn bar(_: bar::Context, x: Box

) { - hprintln!("bar({:?})", x.as_ptr()); + hprintln!("bar({:?})", x.as_ptr()).unwrap(); // this is done automatically so we can omit the call to `drop` // drop(x); diff --git a/examples/preempt.rs b/examples/preempt.rs index 3c7f242990..d0c8cc7d3f 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -25,21 +25,21 @@ mod app { #[task(priority = 1)] fn foo(_: foo::Context) { - hprintln!("foo - start"); + hprintln!("foo - start").unwrap(); baz::spawn().unwrap(); - hprintln!("foo - end"); + hprintln!("foo - end").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn bar(_: bar::Context) { - hprintln!(" bar"); + hprintln!(" bar").unwrap(); } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!(" baz - start"); + hprintln!(" baz - start").unwrap(); bar::spawn().unwrap(); - hprintln!(" baz - end"); + hprintln!(" baz - end").unwrap(); } } diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index 956a2554d8..b3b8012c38 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -1,7 +1,6 @@ //! examples/ramfunc.rs #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -34,7 +33,7 @@ mod app { #[inline(never)] #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index 37a885609f..ae1918d05d 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -56,7 +55,7 @@ mod app { *shared }); - hprintln!("UART0: shared = {}", shared); + hprintln!("UART0: shared = {}", shared).unwrap(); } // `shared` can be accessed from this context @@ -67,6 +66,6 @@ mod app { *shared }); - hprintln!("UART1: shared = {}", shared); + hprintln!("UART1: shared = {}", shared).unwrap(); } } diff --git a/examples/schedule.rs b/examples/schedule.rs index 9b86929d93..5bad5a30ad 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init"); + hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -43,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) bar::spawn_after(1.secs()).unwrap(); @@ -51,7 +50,7 @@ mod app { #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").ok(); // Schedule `baz` to run 1 seconds from now, but with a specific time instant. baz::spawn_at(monotonics::now() + 1.secs()).unwrap(); @@ -59,7 +58,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/shared.rs b/examples/shared.rs index b43a19a3c5..d87dca5263 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -16,9 +15,7 @@ mod app { #[shared] struct Shared { - /// Producer p: Producer<'static, u32, 5>, - /// Consumer c: Consumer<'static, u32, 5>, } @@ -37,7 +34,7 @@ mod app { fn idle(mut c: idle::Context) -> ! { loop { if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { - hprintln!("received message: {}", byte); + hprintln!("received message: {}", byte).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } else { diff --git a/examples/spawn.rs b/examples/spawn.rs index 50ae7e7a75..2db1ab8a28 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); foo::spawn().unwrap(); (Shared {}, Local {}, init::Monotonics()) @@ -28,7 +27,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/static.rs b/examples/static.rs index efafcc7aa8..c9aa6046b5 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -38,7 +37,7 @@ mod app { loop { // Lock-free access to the same underlying queue! if let Some(data) = c.local.c.dequeue() { - hprintln!("received message: {}", data); + hprintln!("received message: {}", data).unwrap(); // Run foo until data if data == 3 { diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 822a2eeabb..12479c0ad4 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 2b17b2ee02..37189faf76 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -1,7 +1,5 @@ -//! examples/t-htask-main.rs #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index 48635b2ab2..1adc9bf044 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -1,7 +1,5 @@ -//! examples/t-idle-main.rs #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index f3979dd62f..5ec420873d 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-spawn.rs b/examples/t-spawn.rs index 7483a8494b..2bd771d7f6 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/task.rs b/examples/task.rs index 9757f2f559..2c53aa2359 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -27,31 +26,31 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo - start"); + hprintln!("foo - start").unwrap(); // spawns `bar` onto the task scheduler // `foo` and `bar` have the same priority so `bar` will not run until // after `foo` terminates bar::spawn().unwrap(); - hprintln!("foo - middle"); + hprintln!("foo - middle").unwrap(); // spawns `baz` onto the task scheduler // `baz` has higher priority than `foo` so it immediately preempts `foo` baz::spawn().unwrap(); - hprintln!("foo - end"); + hprintln!("foo - end").unwrap(); } #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").unwrap(); } } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 309fd8d253..6bcf4fadc8 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -37,7 +37,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, (&r.cfgs, &r.ty, false) } TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), - _ => unreachable!(), }; has_cfgs |= !cfgs.is_empty(); diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 11b92c1b9d..09b2ab3d2c 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1,7 +1,6 @@ #[allow(unused_extern_crates)] extern crate proc_macro; -use core::ops; use proc_macro::TokenStream; use indexmap::{IndexMap, IndexSet}; @@ -23,26 +22,6 @@ pub type Map = IndexMap; /// An order set pub type Set = IndexSet; -/// Immutable pointer -pub struct P { - ptr: Box, -} - -impl P { - /// Boxes `x` making the value immutable - pub fn new(x: T) -> P { - P { ptr: Box::new(x) } - } -} - -impl ops::Deref for P { - type Target = T; - - fn deref(&self) -> &T { - &self.ptr - } -} - /// Execution context #[derive(Clone, Copy)] pub enum Context<'a> { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 06b23f4685..44960b9e8a 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -338,8 +338,8 @@ pub(crate) fn app(app: &App) -> Result { }) } -/// Priority ceiling -pub type Ceiling = Option; +// /// Priority ceiling +// pub type Ceiling = Option; /// Task priority pub type Priority = u8; @@ -427,22 +427,22 @@ pub enum Ownership { }, } -impl Ownership { - /// Whether this resource needs to a lock at this priority level - pub fn needs_lock(&self, priority: u8) -> bool { - match self { - Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, - - Ownership::Contended { ceiling } => { - debug_assert!(*ceiling >= priority); - - priority < *ceiling - } - } - } - - /// Whether this resource is exclusively owned - pub fn is_owned(&self) -> bool { - matches!(self, Ownership::Owned { .. }) - } -} +// impl Ownership { +// /// Whether this resource needs to a lock at this priority level +// pub fn needs_lock(&self, priority: u8) -> bool { +// match self { +// Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, +// +// Ownership::Contended { ceiling } => { +// debug_assert!(*ceiling >= priority); +// +// priority < *ceiling +// } +// } +// } +// +// /// Whether this resource is exclusively owned +// pub fn is_owned(&self) -> bool { +// matches!(self, Ownership::Owned { .. }) +// } +// } diff --git a/src/export.rs b/src/export.rs index 6f2a1b63c1..da4a6917b4 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,11 +1,13 @@ #![allow(clippy::inline_always)] +pub use crate::{ + sll::{IntrusiveSortedLinkedList, Node as IntrusiveNode}, + tq::{TaskNotReady, TimerQueue, WakerNotReady}, +}; +pub use bare_metal::CriticalSection; use core::{ cell::Cell, sync::atomic::{AtomicBool, Ordering}, }; - -pub use crate::tq::{NotReady, TimerQueue}; -pub use bare_metal::CriticalSection; pub use cortex_m::{ asm::nop, asm::wfi, @@ -16,10 +18,134 @@ pub use cortex_m::{ pub use heapless::sorted_linked_list::SortedLinkedList; pub use heapless::spsc::Queue; pub use heapless::BinaryHeap; +pub use heapless::Vec; pub use rtic_monotonic as monotonic; +pub mod idle_executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + fn no_op(_: *const ()) {} + fn no_op_clone(_: *const ()) -> RawWaker { + noop_raw_waker() + } + + static IDLE_WAKER_TABLE: RawWakerVTable = RawWakerVTable::new(no_op_clone, no_op, no_op, no_op); + + #[inline] + fn noop_raw_waker() -> RawWaker { + RawWaker::new(core::ptr::null(), &IDLE_WAKER_TABLE) + } + + pub struct IdleExecutor + where + T: Future, + { + idle: T, + } + + impl IdleExecutor + where + T: Future, + { + #[inline(always)] + pub fn new(idle: T) -> Self { + Self { idle } + } + + #[inline(always)] + pub fn run(&mut self) -> ! { + let w = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut ctxt = Context::from_waker(&w); + loop { + match unsafe { Pin::new_unchecked(&mut self.idle) }.poll(&mut ctxt) { + Poll::Pending => { + // All ok! + } + Poll::Ready(_) => { + // The idle executor will never return + unreachable!() + } + } + } + } + } +} + +pub mod executor { + use core::{ + future::Future, + mem, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + static WAKER_VTABLE: RawWakerVTable = + RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop); + + unsafe fn waker_clone(p: *const ()) -> RawWaker { + RawWaker::new(p, &WAKER_VTABLE) + } + + unsafe fn waker_wake(p: *const ()) { + // The only thing we need from a waker is the function to call to pend the async + // dispatcher. + let f: fn() = mem::transmute(p); + f(); + } + + unsafe fn waker_drop(_: *const ()) { + // nop + } + + //============ + // AsyncTaskExecutor + + pub struct AsyncTaskExecutor { + task: Option, + } + + impl AsyncTaskExecutor { + pub const fn new() -> Self { + Self { task: None } + } + + pub fn is_running(&self) -> bool { + self.task.is_some() + } + + pub fn spawn(&mut self, future: F) { + self.task = Some(future); + } + + pub fn poll(&mut self, wake: fn()) -> bool { + if let Some(future) = &mut self.task { + unsafe { + let waker = Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)); + let mut cx = Context::from_waker(&waker); + let future = Pin::new_unchecked(future); + + match future.poll(&mut cx) { + Poll::Ready(_) => { + self.task = None; + true // Only true if we finished now + } + Poll::Pending => false, + } + } + } else { + false + } + } + } +} + pub type SCFQ = Queue; pub type SCRQ = Queue<(T, u8), N>; +pub type ASYNCRQ = Queue; /// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// It needs to be large enough to cover all the relevant interrupts in use. @@ -117,7 +243,7 @@ impl Priority { /// /// Will overwrite the current Priority #[inline(always)] - pub unsafe fn new(value: u8) -> Self { + pub const unsafe fn new(value: u8) -> Self { Priority { inner: Cell::new(value), } diff --git a/src/lib.rs b/src/lib.rs index 7d12d9af81..da556a5c49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,125 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. +//! +//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the +//! library is `rtic`. +//! +//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic +//! +//! The user level documentation can be found [here]. +//! +//! [here]: https://rtic.rs +//! +//! Don't forget to check the documentation of the `#[app]` attribute (listed under the reexports +//! section), which is the main component of the framework. +//! +//! # Minimum Supported Rust Version (MSRV) +//! +//! This crate is compiled and tested with the latest toolchain (rolling) as of the release date. +//! If you run into compilation errors, try the latest stable release of the rust toolchain. +//! +//! # Semantic Versioning +//! +//! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics +//! require a *semver bump* (since 1.0.0 a new major version release), with the exception of breaking changes +//! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch +//! release. +//! +//! [SemVer]: https://semver.org/spec/v2.0.0.html + +#![deny(missing_docs)] +#![deny(rust_2021_compatibility)] +#![deny(rust_2018_compatibility)] +#![deny(rust_2018_idioms)] +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" +)] +//deny_warnings_placeholder_for_ci +#![allow(clippy::inline_always)] + +use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; +pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; +pub use rtic_macros::app; +pub use rtic_monotonic::{self, Monotonic}; + +/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` +pub mod mutex { + pub use rtic_core::prelude; + pub use rtic_core::Mutex; } -#[cfg(test)] -mod tests { - use super::*; +#[doc(hidden)] +pub mod export; +#[doc(hidden)] +pub mod sll; +#[doc(hidden)] +mod tq; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +/// Sets the given `interrupt` as pending +/// +/// This is a convenience function around +/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend) +pub fn pend(interrupt: I) +where + I: InterruptNumber, +{ + NVIC::pend(interrupt); +} + +use core::cell::UnsafeCell; + +/// Internal replacement for `static mut T` +/// +/// Used to represent RTIC Resources +/// +/// Soundness: +/// 1) Unsafe API for internal use only +/// 2) ``get_mut(&self) -> *mut T`` +/// returns a raw mutable pointer to the inner T +/// casting to &mut T is under control of RTIC +/// RTIC ensures &mut T to be unique under Rust aliasing rules. +/// +/// Implementation uses the underlying ``UnsafeCell`` +/// self.0.get() -> *mut T +/// +/// 3) get(&self) -> *const T +/// returns a raw immutable (const) pointer to the inner T +/// casting to &T is under control of RTIC +/// RTIC ensures &T to be shared under Rust aliasing rules. +/// +/// Implementation uses the underlying ``UnsafeCell`` +/// self.0.get() -> *mut T, demoted to *const T +/// +#[repr(transparent)] +pub struct RacyCell(UnsafeCell); + +impl RacyCell { + /// Create a ``RacyCell`` + #[inline(always)] + pub const fn new(value: T) -> Self { + RacyCell(UnsafeCell::new(value)) + } + + /// Get `*mut T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] + #[inline(always)] + pub unsafe fn get_mut(&self) -> *mut T { + self.0.get() + } + + /// Get `*const T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] + #[inline(always)] + pub unsafe fn get(&self) -> *const T { + self.0.get() } } + +unsafe impl Sync for RacyCell {} diff --git a/src/sll.rs b/src/sll.rs new file mode 100644 index 0000000000..43b53c1749 --- /dev/null +++ b/src/sll.rs @@ -0,0 +1,421 @@ +//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC. +use core::cmp::Ordering; +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; + +/// Marker for Min sorted [`IntrusiveSortedLinkedList`]. +pub struct Min; + +/// Marker for Max sorted [`IntrusiveSortedLinkedList`]. +pub struct Max; + +/// The linked list kind: min-list or max-list +pub trait Kind: private::Sealed { + #[doc(hidden)] + fn ordering() -> Ordering; +} + +impl Kind for Min { + fn ordering() -> Ordering { + Ordering::Less + } +} + +impl Kind for Max { + fn ordering() -> Ordering { + Ordering::Greater + } +} + +/// Sealed traits +mod private { + pub trait Sealed {} +} + +impl private::Sealed for Max {} +impl private::Sealed for Min {} + +/// A node in the [`IntrusiveSortedLinkedList`]. +pub struct Node { + pub val: T, + next: Option>>, +} + +impl Node { + pub fn new(val: T) -> Self { + Self { val, next: None } + } +} + +/// The linked list. +pub struct IntrusiveSortedLinkedList<'a, T, K> { + head: Option>>, + _kind: PhantomData, + _lt: PhantomData<&'a ()>, +} + +impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord + core::fmt::Debug, + K: Kind, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut l = f.debug_list(); + let mut current = self.head; + + while let Some(head) = current { + let head = unsafe { head.as_ref() }; + current = head.next; + + l.entry(&head.val); + } + + l.finish() + } +} + +impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord, + K: Kind, +{ + pub const fn new() -> Self { + Self { + head: None, + _kind: PhantomData, + _lt: PhantomData, + } + } + + // Push to the list. + pub fn push(&mut self, new: &'a mut Node) { + unsafe { + if let Some(head) = self.head { + if head.as_ref().val.cmp(&new.val) != K::ordering() { + // This is newer than head, replace head + new.next = self.head; + self.head = Some(NonNull::new_unchecked(new)); + } else { + // It's not head, search the list for the correct placement + let mut current = head; + + while let Some(next) = current.as_ref().next { + if next.as_ref().val.cmp(&new.val) != K::ordering() { + break; + } + + current = next; + } + + new.next = current.as_ref().next; + current.as_mut().next = Some(NonNull::new_unchecked(new)); + } + } else { + // List is empty, place at head + self.head = Some(NonNull::new_unchecked(new)) + } + } + } + + /// Get an iterator over the sorted list. + pub fn iter(&self) -> Iter<'_, T, K> { + Iter { + _list: self, + index: self.head, + } + } + + /// Find an element in the list that can be changed and resorted. + pub fn find_mut(&mut self, mut f: F) -> Option> + where + F: FnMut(&T) -> bool, + { + let head = self.head?; + + // Special-case, first element + if f(&unsafe { head.as_ref() }.val) { + return Some(FindMut { + is_head: true, + prev_index: None, + index: self.head, + list: self, + maybe_changed: false, + }); + } + + let mut current = head; + + while let Some(next) = unsafe { current.as_ref() }.next { + if f(&unsafe { next.as_ref() }.val) { + return Some(FindMut { + is_head: false, + prev_index: Some(current), + index: Some(next), + list: self, + maybe_changed: false, + }); + } + + current = next; + } + + None + } + + /// Peek at the first element. + pub fn peek(&self) -> Option<&T> { + self.head.map(|head| unsafe { &head.as_ref().val }) + } + + /// Pops the first element in the list. + /// + /// Complexity is worst-case `O(1)`. + pub fn pop(&mut self) -> Option<&'a Node> { + if let Some(head) = self.head { + let v = unsafe { head.as_ref() }; + self.head = v.next; + Some(v) + } else { + None + } + } + + /// Checks if the linked list is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } +} + +/// Iterator for the linked list. +pub struct Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + _list: &'a IntrusiveSortedLinkedList<'a, T, K>, + index: Option>>, +} + +impl<'a, T, K> Iterator for Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + let index = self.index?; + + let node = unsafe { index.as_ref() }; + self.index = node.next; + + Some(&node.val) + } +} + +/// Comes from [`IntrusiveSortedLinkedList::find_mut`]. +pub struct FindMut<'a, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + list: &'a mut IntrusiveSortedLinkedList<'b, T, K>, + is_head: bool, + prev_index: Option>>, + index: Option>>, + maybe_changed: bool, +} + +impl<'a, 'b, T, K> FindMut<'a, 'b, T, K> +where + T: Ord, + K: Kind, +{ + unsafe fn pop_internal(&mut self) -> &'b mut Node { + if self.is_head { + // If it is the head element, we can do a normal pop + let mut head = self.list.head.unwrap_unchecked(); + let v = head.as_mut(); + self.list.head = v.next; + v + } else { + // Somewhere in the list + let mut prev = self.prev_index.unwrap_unchecked(); + let mut curr = self.index.unwrap_unchecked(); + + // Re-point the previous index + prev.as_mut().next = curr.as_ref().next; + + curr.as_mut() + } + } + + /// This will pop the element from the list. + /// + /// Complexity is worst-case `O(1)`. + #[inline] + pub fn pop(mut self) -> &'b mut Node { + unsafe { self.pop_internal() } + } + + /// This will resort the element into the correct position in the list if needed. The resorting + /// will only happen if the element has been accessed mutably. + /// + /// Same as calling `drop`. + /// + /// Complexity is worst-case `O(N)`. + #[inline] + pub fn finish(self) { + drop(self) + } +} + +impl<'b, T, K> Drop for FindMut<'_, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + fn drop(&mut self) { + // Only resort the list if the element has changed + if self.maybe_changed { + unsafe { + let val = self.pop_internal(); + self.list.push(val); + } + } + } +} + +impl Deref for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &self.index.unwrap_unchecked().as_ref().val } + } +} + +impl DerefMut for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.maybe_changed = true; + unsafe { &mut self.index.unwrap_unchecked().as_mut().val } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn const_new() { + static mut _V1: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + } + + #[test] + fn test_peek() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &3); + + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + } + + #[test] + fn test_empty() { + let ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + assert!(ll.is_empty()) + } + + #[test] + fn test_updating() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 2).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1002); + + let mut find = ll.find_mut(|v| *v == 3).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1003); + + // Remove largest element + ll.find_mut(|v| *v == 1003).unwrap().pop(); + + assert_eq!(ll.peek().unwrap(), &1002); + } + + #[test] + fn test_updating_1() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let v = ll.pop().unwrap(); + + assert_eq!(v.val, 1); + } + + #[test] + fn test_updating_2() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 1).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1001); + } +} diff --git a/src/tq.rs b/src/tq.rs index 0f585ba4dd..daa91c8d48 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,29 +1,28 @@ -use crate::Monotonic; +use crate::{ + sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode}, + Monotonic, +}; use core::cmp::Ordering; -use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList}; +use core::task::Waker; +use heapless::sorted_linked_list::{LinkedIndexU16, Min as SllMin, SortedLinkedList}; -pub struct TimerQueue( - pub SortedLinkedList, LinkedIndexU16, Min, N>, -) -where - Mono: Monotonic, - Task: Copy; - -impl TimerQueue +pub struct TimerQueue<'a, Mono, Task, const N_TASK: usize> where Mono: Monotonic, Task: Copy, { - /// # Safety - /// - /// Writing to memory with a transmute in order to enable - /// interrupts of the ``SysTick`` timer - /// - /// Enqueue a task without checking if it is full - #[inline] - pub unsafe fn enqueue_unchecked( - &mut self, - nr: NotReady, + pub task_queue: SortedLinkedList, LinkedIndexU16, SllMin, N_TASK>, + pub waker_queue: IntrusiveSortedLinkedList<'a, WakerNotReady, IsslMin>, +} + +impl<'a, Mono, Task, const N_TASK: usize> TimerQueue<'a, Mono, Task, N_TASK> +where + Mono: Monotonic + 'a, + Task: Copy, +{ + fn check_if_enable( + &self, + instant: Mono::Instant, enable_interrupt: F1, pend_handler: F2, mono: Option<&mut Mono>, @@ -33,11 +32,17 @@ where { // Check if the top contains a non-empty element and if that element is // greater than nr - let if_heap_max_greater_than_nr = - self.0.peek().map_or(true, |head| nr.instant < head.instant); + let if_task_heap_max_greater_than_nr = self + .task_queue + .peek() + .map_or(true, |head| instant < head.instant); + let if_waker_heap_max_greater_than_nr = self + .waker_queue + .peek() + .map_or(true, |head| instant < head.instant); - if if_heap_max_greater_than_nr { - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() { + if if_task_heap_max_greater_than_nr || if_waker_heap_max_greater_than_nr { + if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.is_empty() { if let Some(mono) = mono { mono.enable_timer(); } @@ -46,19 +51,49 @@ where pend_handler(); } - - self.0.push_unchecked(nr); } - /// Check if the timer queue is empty. + /// Enqueue a task without checking if it is full + #[inline] + pub unsafe fn enqueue_task_unchecked( + &mut self, + nr: TaskNotReady, + enable_interrupt: F1, + pend_handler: F2, + mono: Option<&mut Mono>, + ) where + F1: FnOnce(), + F2: FnOnce(), + { + self.check_if_enable(nr.instant, enable_interrupt, pend_handler, mono); + self.task_queue.push_unchecked(nr); + } + + /// Enqueue a waker + #[inline] + pub fn enqueue_waker( + &mut self, + nr: &'a mut IntrusiveNode>, + enable_interrupt: F1, + pend_handler: F2, + mono: Option<&mut Mono>, + ) where + F1: FnOnce(), + F2: FnOnce(), + { + self.check_if_enable(nr.val.instant, enable_interrupt, pend_handler, mono); + self.waker_queue.push(nr); + } + + /// Check if all the timer queue is empty. #[inline] pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.task_queue.is_empty() && self.waker_queue.is_empty() } - /// Cancel the marker value - pub fn cancel_marker(&mut self, marker: u32) -> Option<(Task, u8)> { - if let Some(val) = self.0.find_mut(|nr| nr.marker == marker) { + /// Cancel the marker value for a task + pub fn cancel_task_marker(&mut self, marker: u32) -> Option<(Task, u8)> { + if let Some(val) = self.task_queue.find_mut(|nr| nr.marker == marker) { let nr = val.pop(); Some((nr.task, nr.index)) @@ -67,16 +102,23 @@ where } } - /// Update the instant at an marker value to a new instant + /// Cancel the marker value for a waker + pub fn cancel_waker_marker(&mut self, marker: u32) { + if let Some(val) = self.waker_queue.find_mut(|nr| nr.marker == marker) { + let _ = val.pop(); + } + } + + /// Update the instant at an marker value for a task to a new instant #[allow(clippy::result_unit_err)] - pub fn update_marker( + pub fn update_task_marker( &mut self, marker: u32, new_marker: u32, instant: Mono::Instant, pend_handler: F, ) -> Result<(), ()> { - if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) { + if let Some(mut val) = self.task_queue.find_mut(|nr| nr.marker == marker) { val.instant = instant; val.marker = new_marker; @@ -89,6 +131,62 @@ where } } + fn dequeue_task_queue( + &mut self, + instant: Mono::Instant, + mono: &mut Mono, + ) -> Option<(Task, u8)> { + if instant <= mono.now() { + // task became ready + let nr = unsafe { self.task_queue.pop_unchecked() }; + Some((nr.task, nr.index)) + } else { + // Set compare + mono.set_compare(instant); + + // Double check that the instant we set is really in the future, else + // 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 + // guard against this. + if instant <= mono.now() { + let nr = unsafe { self.task_queue.pop_unchecked() }; + Some((nr.task, nr.index)) + } else { + None + } + } + } + + fn dequeue_waker_queue(&mut self, instant: Mono::Instant, mono: &mut Mono) -> bool { + let mut did_wake = false; + + if instant <= mono.now() { + // Task became ready, wake the waker + if let Some(v) = self.waker_queue.pop() { + v.val.waker.wake_by_ref(); + + did_wake = true; + } + } else { + // Set compare + mono.set_compare(instant); + + // Double check that the instant we set is really in the future, else + // 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 + // guard against this. + if instant <= mono.now() { + if let Some(v) = self.waker_queue.pop() { + v.val.waker.wake_by_ref(); + + did_wake = true; + } + } + } + + did_wake + } + /// Dequeue a task from the ``TimerQueue`` pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> where @@ -96,59 +194,72 @@ where { mono.clear_compare_flag(); - if let Some(instant) = self.0.peek().map(|p| p.instant) { - if instant <= mono.now() { - // task became ready - let nr = unsafe { self.0.pop_unchecked() }; + loop { + let tq = self.task_queue.peek().map(|p| p.instant); + let wq = self.waker_queue.peek().map(|p| p.instant); - Some((nr.task, nr.index)) - } else { - // Set compare - mono.set_compare(instant); + let dequeue_task; + let instant; - // Double check that the instant we set is really in the future, else - // 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 - // guard against this. - if instant <= mono.now() { - let nr = unsafe { self.0.pop_unchecked() }; + match (tq, wq) { + (Some(tq_instant), Some(wq_instant)) => { + if tq_instant <= wq_instant { + dequeue_task = true; + instant = tq_instant; + } else { + dequeue_task = false; + instant = wq_instant; + } + } + (Some(tq_instant), None) => { + dequeue_task = true; + instant = tq_instant; + } + (None, Some(wq_instant)) => { + dequeue_task = false; + instant = wq_instant; + } + (None, None) => { + // The queue is empty, disable the interrupt. + if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { + disable_interrupt(); + mono.disable_timer(); + } - Some((nr.task, nr.index)) - } else { - None + return None; } } - } else { - // The queue is empty, disable the interrupt. - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - disable_interrupt(); - mono.disable_timer(); - } - None + if dequeue_task { + return self.dequeue_task_queue(instant, mono); + } else if !self.dequeue_waker_queue(instant, mono) { + return None; + } else { + // Run the dequeue again + } } } } -pub struct NotReady +pub struct TaskNotReady where Task: Copy, Mono: Monotonic, { + pub task: Task, pub index: u8, pub instant: Mono::Instant, - pub task: Task, pub marker: u32, } -impl Eq for NotReady +impl Eq for TaskNotReady where Task: Copy, Mono: Monotonic, { } -impl Ord for NotReady +impl Ord for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -158,7 +269,7 @@ where } } -impl PartialEq for NotReady +impl PartialEq for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -168,7 +279,7 @@ where } } -impl PartialOrd for NotReady +impl PartialOrd for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -177,3 +288,41 @@ where Some(self.cmp(other)) } } + +pub struct WakerNotReady +where + Mono: Monotonic, +{ + pub waker: Waker, + pub instant: Mono::Instant, + pub marker: u32, +} + +impl Eq for WakerNotReady where Mono: Monotonic {} + +impl Ord for WakerNotReady +where + Mono: Monotonic, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.instant.cmp(&other.instant) + } +} + +impl PartialEq for WakerNotReady +where + Mono: Monotonic, +{ + fn eq(&self, other: &Self) -> bool { + self.instant == other.instant + } +} + +impl PartialOrd for WakerNotReady +where + Mono: Monotonic, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr index a667c58824..d8c01b9a1a 100644 --- a/ui/extern-interrupt-not-enough.stderr +++ b/ui/extern-interrupt-not-enough.stderr @@ -1,5 +1,5 @@ -error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) - --> $DIR/extern-interrupt-not-enough.rs:17:8 +error: not enough interrupts to dispatch all software and async tasks (need: 1; given: 0) - one interrupt is needed per priority and sync/async task + --> ui/extern-interrupt-not-enough.rs:17:8 | 17 | fn a(_: a::Context) {} | ^ diff --git a/ui/task-priority-too-high.rs b/ui/task-priority-too-high.rs index e7e0cce207..46ab561750 100644 --- a/ui/task-priority-too-high.rs +++ b/ui/task-priority-too-high.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { (Shared {}, Local {}, init::Monotonics()) } diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index 026124c8fa..a7a15ebfe5 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -1,3 +1,11 @@ +warning: unused variable: `cx` + --> ui/task-priority-too-high.rs:12:13 + | +12 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + | ^^ help: if this is intentional, prefix it with an underscore: `_cx` + | + = note: `#[warn(unused_variables)]` on by default + error[E0080]: evaluation of constant value failed --> ui/task-priority-too-high.rs:3:1 | diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 100888c075..889540c529 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -47,6 +47,7 @@ impl<'a> CargoCommand<'a> { mode, } => { let mut args = vec![ + "+nightly", self.name(), "--example", example, @@ -69,7 +70,7 @@ impl<'a> CargoCommand<'a> { features, mode, } => { - let mut args = vec![self.name(), "--examples", "--target", target]; + let mut args = vec!["+nightly", self.name(), "--examples", "--target", target]; if let Some(feature_name) = features { args.extend_from_slice(&["--features", feature_name]);