From 1f51b10297e9cbb4797aa1ed8be6a2b84c9f2e07 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 28 Jan 2023 21:57:43 +0100 Subject: Book: Major rework for RTIC v2 --- book/en/src/by-example/channel.md | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 book/en/src/by-example/channel.md (limited to 'book/en/src/by-example/channel.md') diff --git a/book/en/src/by-example/channel.md b/book/en/src/by-example/channel.md new file mode 100644 index 0000000..99bfedd --- /dev/null +++ b/book/en/src/by-example/channel.md @@ -0,0 +1,112 @@ +# Communication over channels. + +Channels can be used to communicate data between running *software* tasks. The channel is essentially a wait queue, allowing tasks with multiple producers and a single receiver. A channel is constructed in the `init` task and backed by statically allocated memory. Send and receive endpoints are distributed to *software* tasks: + +```rust +... +const CAPACITY: usize = 5; +#[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, CAPACITY); + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + sender2::spawn(s.clone()).unwrap(); + ... +``` + +In this case the channel holds data of `u32` type with a capacity of 5 elements. + +## Sending data + +The `send` method post a message on the channel as shown below: + +```rust +#[task] +async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); +} +``` + +## Receiving data + +The receiver can `await` incoming messages: + +```rust +#[task] +async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + ... + } +} +``` + +For a complete example: + +``` rust +{{#include ../../../../rtic/examples/async-channel.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel.run}} +``` + +Also sender endpoint can be awaited. In case there the channel capacity has not been reached, `await` the sender can progress immediately, while in the case the capacity is reached, the sender is blocked until there is free space in the queue. In this way data is never lost. + +In the below example the `CAPACITY` has been reduced to 1, forcing sender tasks to wait until the data in the channel has been received. + +``` rust +{{#include ../../../../rtic/examples/async-channel-done.rs}} +``` + +Looking at the output, we find that `Sender 2` will wait until the data sent by `Sender 1` as been received. + +> **NOTICE** *Software* tasks at the same priority are executed asynchronously to each other, thus **NO** strict order can be assumed. (The presented order here applies only to the current implementation, and may change between RTIC framework releases.) + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-done --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-done.run}} +``` + +## Error handling + +In case all senders have been dropped `await` on an empty receiver channel results in an error. This allows to gracefully implement different types of shutdown operations. + +``` rust +{{#include ../../../../rtic/examples/async-channel-no-sender.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-no-sender --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-no-sender.run}} +``` + +Similarly, `await` on a send channel results in an error in case the receiver has been dropped. This allows to gracefully implement application level error handling. + +The resulting error returns the data back to the sender, allowing the sender to take appropriate action (e.g., storing the data to later retry sending it). + +``` rust +{{#include ../../../../rtic/examples/async-channel-no-receiver.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-no-receiver --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-no-receiver.run}} +``` + + + +## Try API + +In cases you wish the sender to proceed even in case the channel is full. To that end, a `try_send` API is provided. + +``` rust +{{#include ../../../../rtic/examples/async-channel-try.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-try --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-try.run}} +``` \ No newline at end of file -- cgit v1.2.3 From 89632f9b22d33bef08b2f98554e263c8a1d7cfa0 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Wed, 1 Feb 2023 19:46:58 +0100 Subject: book polish --- book/en/src/by-example/channel.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'book/en/src/by-example/channel.md') diff --git a/book/en/src/by-example/channel.md b/book/en/src/by-example/channel.md index 99bfedd..1f9510a 100644 --- a/book/en/src/by-example/channel.md +++ b/book/en/src/by-example/channel.md @@ -2,7 +2,7 @@ Channels can be used to communicate data between running *software* tasks. The channel is essentially a wait queue, allowing tasks with multiple producers and a single receiver. A channel is constructed in the `init` task and backed by statically allocated memory. Send and receive endpoints are distributed to *software* tasks: -```rust +``` rust ... const CAPACITY: usize = 5; #[init] @@ -20,7 +20,7 @@ In this case the channel holds data of `u32` type with a capacity of 5 elements The `send` method post a message on the channel as shown below: -```rust +``` rust #[task] async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { hprintln!("Sender 1 sending: 1"); @@ -32,7 +32,7 @@ async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY The receiver can `await` incoming messages: -```rust +``` rust #[task] async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { while let Ok(val) = receiver.recv().await { @@ -42,6 +42,8 @@ async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CA } ``` +Channels are implemented using a small (global) *Critical Section* (CS) for protection against race-conditions. The user must provide an CS implementation. Compiling the examples given the `--features test-critical-section` gives one possible implementation. + For a complete example: ``` rust @@ -50,6 +52,9 @@ For a complete example: ``` console $ cargo run --target thumbv7m-none-eabi --example async-channel --features test-critical-section +``` + +``` console {{#include ../../../../rtic/ci/expected/async-channel.run}} ``` @@ -79,7 +84,10 @@ In case all senders have been dropped `await` on an empty receiver channel resul ``` ``` console -$ cargo run --target thumbv7m-none-eabi --example async-channel-no-sender --features test-critical-section +$ cargo run --target thumbv7m-none-eabi --example async-channel-no-sender --features test-critical-section +``` + +``` console {{#include ../../../../rtic/ci/expected/async-channel-no-sender.run}} ``` @@ -93,6 +101,9 @@ The resulting error returns the data back to the sender, allowing the sender to ``` console $ cargo run --target thumbv7m-none-eabi --example async-channel-no-receiver --features test-critical-section +``` + +``` console {{#include ../../../../rtic/ci/expected/async-channel-no-receiver.run}} ``` @@ -107,6 +118,9 @@ In cases you wish the sender to proceed even in case the channel is full. To tha ``` ``` console -$ cargo run --target thumbv7m-none-eabi --example async-channel-try --features test-critical-section +$ cargo run --target thumbv7m-none-eabi --example async-channel-try --features test-critical-section +``` + +``` console {{#include ../../../../rtic/ci/expected/async-channel-try.run}} ``` \ No newline at end of file -- cgit v1.2.3 From fc6343b65c79b287ba1884514698e59f87a3d47d Mon Sep 17 00:00:00 2001 From: perlindgren Date: Wed, 1 Feb 2023 22:37:42 +0100 Subject: Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks for all suggestions, awesome! Co-authored-by: Henrik Tjäder --- book/en/src/by-example/channel.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'book/en/src/by-example/channel.md') diff --git a/book/en/src/by-example/channel.md b/book/en/src/by-example/channel.md index 1f9510a..c020870 100644 --- a/book/en/src/by-example/channel.md +++ b/book/en/src/by-example/channel.md @@ -58,9 +58,9 @@ $ cargo run --target thumbv7m-none-eabi --example async-channel --features test- {{#include ../../../../rtic/ci/expected/async-channel.run}} ``` -Also sender endpoint can be awaited. In case there the channel capacity has not been reached, `await` the sender can progress immediately, while in the case the capacity is reached, the sender is blocked until there is free space in the queue. In this way data is never lost. +Also sender endpoint can be awaited. In case the channel capacity has not yet been reached, `await`-ing the sender can progress immediately, while in the case the capacity is reached, the sender is blocked until there is free space in the queue. In this way data is never lost. -In the below example the `CAPACITY` has been reduced to 1, forcing sender tasks to wait until the data in the channel has been received. +In the following example the `CAPACITY` has been reduced to 1, forcing sender tasks to wait until the data in the channel has been received. ``` rust {{#include ../../../../rtic/examples/async-channel-done.rs}} @@ -77,7 +77,7 @@ $ cargo run --target thumbv7m-none-eabi --example async-channel-done --features ## Error handling -In case all senders have been dropped `await` on an empty receiver channel results in an error. This allows to gracefully implement different types of shutdown operations. +In case all senders have been dropped `await`-ing on an empty receiver channel results in an error. This allows to gracefully implement different types of shutdown operations. ``` rust {{#include ../../../../rtic/examples/async-channel-no-sender.rs}} @@ -91,7 +91,7 @@ $ cargo run --target thumbv7m-none-eabi --example async-channel-no-sender --feat {{#include ../../../../rtic/ci/expected/async-channel-no-sender.run}} ``` -Similarly, `await` on a send channel results in an error in case the receiver has been dropped. This allows to gracefully implement application level error handling. +Similarly, `await`-ing on a send channel results in an error in case the receiver has been dropped. This allows to gracefully implement application level error handling. The resulting error returns the data back to the sender, allowing the sender to take appropriate action (e.g., storing the data to later retry sending it). -- cgit v1.2.3