aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ci/expected/async-delay.run7
-rw-r--r--ci/expected/async-infinite-loop.run6
-rw-r--r--ci/expected/async-task-multiple-prios.run5
-rw-r--r--ci/expected/async-task.run3
-rw-r--r--ci/expected/async-timeout.run5
-rw-r--r--ci/expected/periodic-at.run6
-rw-r--r--ci/expected/periodic-at2.run10
-rw-r--r--examples/async-delay.rs67
-rw-r--r--examples/async-infinite-loop.rs57
-rw-r--r--examples/async-task-multiple-prios.rs76
-rw-r--r--examples/async-task.rs61
-rw-r--r--examples/async-timeout.rs87
-rw-r--r--examples/binds.rs13
-rw-r--r--examples/cancel-reschedule.rs9
-rw-r--r--examples/capacity.rs5
-rw-r--r--examples/cfg-whole-task.rs17
-rw-r--r--examples/common.rs7
-rw-r--r--examples/complex.rs54
-rw-r--r--examples/declared_locals.rs1
-rw-r--r--examples/destructure.rs5
-rw-r--r--examples/extern_binds.rs12
-rw-r--r--examples/extern_spawn.rs3
-rw-r--r--examples/generics.rs10
-rw-r--r--examples/hardware.rs13
-rw-r--r--examples/idle-wfi.rs5
-rw-r--r--examples/idle.rs5
-rw-r--r--examples/init.rs3
-rw-r--r--examples/locals.rs11
-rw-r--r--examples/lock-free.rs5
-rw-r--r--examples/lock.rs11
-rw-r--r--examples/message.rs7
-rw-r--r--examples/message_passing.rs3
-rw-r--r--examples/multilock.rs3
-rw-r--r--examples/not-sync.rs4
-rw-r--r--examples/only-shared-access.rs5
-rw-r--r--examples/periodic-at.rs7
-rw-r--r--examples/periodic-at2.rs11
-rw-r--r--examples/periodic.rs9
-rw-r--r--examples/peripherals-taken.rs4
-rw-r--r--examples/pool.rs2
-rw-r--r--examples/preempt.rs10
-rw-r--r--examples/ramfunc.rs3
-rw-r--r--examples/resource-user-struct.rs5
-rw-r--r--examples/schedule.rs9
-rw-r--r--examples/shared.rs5
-rw-r--r--examples/spawn.rs5
-rw-r--r--examples/static.rs3
-rw-r--r--examples/t-binds.rs1
-rw-r--r--examples/t-htask-main.rs2
-rw-r--r--examples/t-idle-main.rs2
-rw-r--r--examples/t-schedule.rs1
-rw-r--r--examples/t-spawn.rs1
-rw-r--r--examples/task.rs11
-rw-r--r--macros/src/codegen/local_resources_struct.rs1
-rw-r--r--macros/src/syntax.rs21
-rw-r--r--macros/src/syntax/analyze.rs42
-rw-r--r--src/export.rs134
-rw-r--r--src/lib.rs129
-rw-r--r--src/sll.rs421
-rw-r--r--src/tq.rs275
-rw-r--r--ui/extern-interrupt-not-enough.stderr4
-rw-r--r--ui/task-priority-too-high.rs2
-rw-r--r--ui/task-priority-too-high.stderr8
-rw-r--r--xtask/src/command.rs3
64 files changed, 1417 insertions, 315 deletions
diff --git a/ci/expected/async-delay.run b/ci/expected/async-delay.run
new file mode 100644
index 00000000..61852abf
--- /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 00000000..f9fd4e49
--- /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 00000000..9b0f5336
--- /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 00000000..f7ce3a60
--- /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 00000000..a8074230
--- /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 54020f9e..bf5bb063 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 47adbef4..6e56421a 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/examples/async-delay.rs b/examples/async-delay.rs
new file mode 100644
index 00000000..7802bda4
--- /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 00000000..7615818d
--- /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 00000000..3e197987
--- /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 00000000..4d25ec44
--- /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 00000000..3f68df74
--- /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<Self::Output> {
+ // Never finish
+ Poll::Pending
+ }
+ }
+}
diff --git a/examples/binds.rs b/examples/binds.rs
index 1b0c8c5b..56565cbe 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 36c496b7..a38a9c4e 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 550829be..a6172698 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 17f31f4e..f41866db 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 74ee8db2..1fe671e6 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 73df025d..e5cf6dbe 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 cb621496..52d354bc 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 70b0dd7e..6019c225 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 bfc85cfc..4dc6633c 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 446d31a7..7f9b5a5f 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 bc4959fb..72b861ba 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<T = u32>) {
(old, *shared)
});
- hprintln!("shared: {} -> {}", old, new);
+ hprintln!("shared: {} -> {}", old, new).unwrap();
}
diff --git a/examples/hardware.rs b/examples/hardware.rs
index a7fdb47a..60632247 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 5e52620d..4a8a8dee 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 ccec9bf2..55d6b153 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 afd3b98c..b8a5bc5b 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 9e112be4..aa5d0fee 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 6e5faadb..ea6ff1bf 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 5b3e0bcc..f1a16968 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 8a6a12d5..76c5675a 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 9550a501..ffa95371 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 c7085cd5..d99bae69 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 68af04a6..aa79ad56 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 b32827ab..8b0a77ef 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 ad8a5496..ca68ed5e 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 4719bdb7..ec9adcc5 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: <MyMono as rtic_monotonic::Monotonic>::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 13ca7c85..2f9e8e6a 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 cc9b9a11..d542c0e6 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 4c551bef..5aadd24c 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]
diff --git a/examples/preempt.rs b/examples/preempt.rs
index 3c7f2429..d0c8cc7d 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 956a2554..b3b8012c 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 37a88560..ae1918d0 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 9b86929d..5bad5a30 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 b43a19a3..d87dca52 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 50ae7e7a..2db1ab8a 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 efafcc7a..c9aa6046 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 822a2eea..12479c0a 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 2b17b2ee..37189faf 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 48635b2a..1adc9bf0 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 f3979dd6..5ec42087 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 7483a849..2bd771d7 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 9757f2f5..2c53aa23 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 309fd8d2..6bcf4fad 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 11b92c1b..09b2ab3d 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<T> = IndexMap<Ident, T>;
/// An order set
pub type Set<T> = IndexSet<T>;
-/// Immutable pointer
-pub struct P<T> {
- ptr: Box<T>,
-}
-
-impl<T> P<T> {
- /// Boxes `x` making the value immutable
- pub fn new(x: T) -> P<T> {
- P { ptr: Box::new(x) }
- }
-}
-
-impl<T> ops::Deref for P<T> {
- 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 06b23f46..44960b9e 100644
--- a/macros/src/syntax/analyze.rs
+++ b/macros/src/syntax/analyze.rs
@@ -338,8 +338,8 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
})
}
-/// Priority ceiling
-pub type Ceiling = Option<u8>;
+// /// Priority ceiling
+// pub type Ceiling = Option<u8>;
/// 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 6f2a1b63..da4a6917 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<T>
+ where
+ T: Future,
+ {
+ idle: T,
+ }
+
+ impl<T> IdleExecutor<T>
+ 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<F: Future + 'static> {
+ task: Option<F>,
+ }
+
+ impl<F: Future + 'static> AsyncTaskExecutor<F> {
+ 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<const N: usize> = Queue<u8, N>;
pub type SCRQ<T, const N: usize> = Queue<(T, u8), N>;
+pub type ASYNCRQ<T, const N: usize> = Queue<T, N>;
/// 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 7d12d9af..da556a5c 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;
+}
+
+#[doc(hidden)]
+pub mod export;
+#[doc(hidden)]
+pub mod sll;
+#[doc(hidden)]
+mod tq;
+
+/// 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<I>(interrupt: I)
+where
+ I: InterruptNumber,
+{
+ NVIC::pend(interrupt);
}
-#[cfg(test)]
-mod tests {
- use super::*;
+use core::cell::UnsafeCell;
- #[test]
- fn it_works() {
- let result = add(2, 2);
- assert_eq!(result, 4);
+/// 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<T>``
+/// 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<T>``
+/// self.0.get() -> *mut T, demoted to *const T
+///
+#[repr(transparent)]
+pub struct RacyCell<T>(UnsafeCell<T>);
+
+impl<T> RacyCell<T> {
+ /// 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<T> Sync for RacyCell<T> {}
diff --git a/src/sll.rs b/src/sll.rs
new file mode 100644
index 00000000..43b53c17
--- /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<T> {
+ pub val: T,
+ next: Option<NonNull<Node<T>>>,
+}
+
+impl<T> Node<T> {
+ pub fn new(val: T) -> Self {
+ Self { val, next: None }
+ }
+}
+
+/// The linked list.
+pub struct IntrusiveSortedLinkedList<'a, T, K> {
+ head: Option<NonNull<Node<T>>>,
+ _kind: PhantomData<K>,
+ _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<T>) {
+ 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<F>(&mut self, mut f: F) -> Option<FindMut<'_, 'a, T, K>>
+ 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<T>> {
+ 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<NonNull<Node<T>>>,
+}
+
+impl<'a, T, K> Iterator for Iter<'a, T, K>
+where
+ T: Ord,
+ K: Kind,
+{
+ type Item = &'a T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ 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<NonNull<Node<T>>>,
+ index: Option<NonNull<Node<T>>>,
+ 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<T> {
+ 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<T> {
+ 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<T, K> 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<T, K> 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<u32, Max> = IntrusiveSortedLinkedList::new();
+ }
+
+ #[test]
+ fn test_peek() {
+ let mut ll: IntrusiveSortedLinkedList<u32, Max> = 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<u32, Min> = 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<u32, Max> = IntrusiveSortedLinkedList::new();
+
+ assert!(ll.is_empty())
+ }
+
+ #[test]
+ fn test_updating() {
+ let mut ll: IntrusiveSortedLinkedList<u32, Max> = 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<u32, Max> = 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<u32, Max> = 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 0f585ba4..daa91c8d 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<Mono, Task, const N: usize>(
- pub SortedLinkedList<NotReady<Mono, Task>, LinkedIndexU16, Min, N>,
-)
+pub struct TimerQueue<'a, Mono, Task, const N_TASK: usize>
where
Mono: Monotonic,
- Task: Copy;
+ Task: Copy,
+{
+ pub task_queue: SortedLinkedList<TaskNotReady<Mono, Task>, LinkedIndexU16, SllMin, N_TASK>,
+ pub waker_queue: IntrusiveSortedLinkedList<'a, WakerNotReady<Mono>, IsslMin>,
+}
-impl<Mono, Task, const N: usize> TimerQueue<Mono, Task, N>
+impl<'a, Mono, Task, const N_TASK: usize> TimerQueue<'a, Mono, Task, N_TASK>
where
- Mono: Monotonic,
+ Mono: Monotonic + 'a,
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<F1, F2>(
- &mut self,
- nr: NotReady<Mono, Task>,
+ fn check_if_enable<F1, F2>(
+ &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);
+ /// Enqueue a task without checking if it is full
+ #[inline]
+ pub unsafe fn enqueue_task_unchecked<F1, F2>(
+ &mut self,
+ nr: TaskNotReady<Mono, Task>,
+ 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);
}
- /// Check if the timer queue is empty.
+ /// Enqueue a waker
+ #[inline]
+ pub fn enqueue_waker<F1, F2>(
+ &mut self,
+ nr: &'a mut IntrusiveNode<WakerNotReady<Mono>>,
+ 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<F: FnOnce()>(
+ pub fn update_task_marker<F: FnOnce()>(
&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<F>(&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);
-
- // 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() };
-
- Some((nr.task, nr.index))
- } else {
- None
+ let dequeue_task;
+ let instant;
+
+ 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();
+ }
+
+ 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<Mono, Task>
+pub struct TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
{
+ pub task: Task,
pub index: u8,
pub instant: Mono::Instant,
- pub task: Task,
pub marker: u32,
}
-impl<Mono, Task> Eq for NotReady<Mono, Task>
+impl<Mono, Task> Eq for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
{
}
-impl<Mono, Task> Ord for NotReady<Mono, Task>
+impl<Mono, Task> Ord for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@@ -158,7 +269,7 @@ where
}
}
-impl<Mono, Task> PartialEq for NotReady<Mono, Task>
+impl<Mono, Task> PartialEq for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@@ -168,7 +279,7 @@ where
}
}
-impl<Mono, Task> PartialOrd for NotReady<Mono, Task>
+impl<Mono, Task> PartialOrd for TaskNotReady<Mono, Task>
where
Task: Copy,
Mono: Monotonic,
@@ -177,3 +288,41 @@ where
Some(self.cmp(other))
}
}
+
+pub struct WakerNotReady<Mono>
+where
+ Mono: Monotonic,
+{
+ pub waker: Waker,
+ pub instant: Mono::Instant,
+ pub marker: u32,
+}
+
+impl<Mono> Eq for WakerNotReady<Mono> where Mono: Monotonic {}
+
+impl<Mono> Ord for WakerNotReady<Mono>
+where
+ Mono: Monotonic,
+{
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.instant.cmp(&other.instant)
+ }
+}
+
+impl<Mono> PartialEq for WakerNotReady<Mono>
+where
+ Mono: Monotonic,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.instant == other.instant
+ }
+}
+
+impl<Mono> PartialOrd for WakerNotReady<Mono>
+where
+ Mono: Monotonic,
+{
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr
index a667c588..d8c01b9a 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 e7e0cce2..46ab5617 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 026124c8..a7a15ebf 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 100888c0..889540c5 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]);