diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 11ba4cef90..855cde93a6 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -97,11 +97,15 @@ $ cargo run --example only-shared-access ## Lock-free resource access of mutable resources -There exists two other options dealing with resources +A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. +In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). +Note that this is merely a convenience: if you do use the `lock` API, at runtime the framework will *not* produce a critical section. -* `#[lock_free]`: there might be several tasks with the same priority - accessing the resource without critical section. Since tasks with the - same priority never can preempt another task on the same priority - this is safe. -* `#[task_local]`: there must be only one task using this resource, - similar to a `static mut` task local resource, but (optionally) set-up by init. +``` rust +{{#include ../../../../examples/lock-free.rs}} +``` + +``` console +$ cargo run --example lock-free +{{#include ../../../../ci/expected/lock-free.run}} +``` diff --git a/ci/expected/lock-free.run b/ci/expected/lock-free.run new file mode 100644 index 0000000000..56f47a0be4 --- /dev/null +++ b/ci/expected/lock-free.run @@ -0,0 +1,14 @@ +GPIOA/start + GPIOA/counter = 1 +GPIOA/end +GPIOB/start + GPIOB/counter = 2 +GPIOB/end +GPIOA/start + GPIOA/counter = 3 +GPIOA/end +GPIOB/start + GPIOB/counter = 4 +GPIOB/end +GPIOA/start + GPIOA/counter = 5 diff --git a/examples/lock-free.rs b/examples/lock-free.rs new file mode 100644 index 0000000000..db74c7d8b0 --- /dev/null +++ b/examples/lock-free.rs @@ -0,0 +1,60 @@ +//! examples/lock-free.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use lm3s6965::Interrupt; + + #[shared] + struct Shared { + #[lock_free] // <- lock-free shared resource + counter: u64, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + rtic::pend(Interrupt::GPIOA); + + (Shared { counter: 0 }, Local {}, init::Monotonics()) + } + + #[task(binds = GPIOA, shared = [counter])] // <- same priority + fn gpioa(c: gpioa::Context) { + hprintln!("GPIOA/start").unwrap(); + rtic::pend(Interrupt::GPIOB); + + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" GPIOA/counter = {}", counter).unwrap(); + + if counter == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + hprintln!("GPIOA/end").unwrap(); + } + + #[task(binds = GPIOB, shared = [counter])] // <- same priority + fn gpiob(c: gpiob::Context) { + hprintln!("GPIOB/start").unwrap(); + rtic::pend(Interrupt::GPIOA); + + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" GPIOB/counter = {}", counter).unwrap(); + + if counter == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + hprintln!("GPIOB/end").unwrap(); + } +}