diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index c0bfc56e14..d26b328387 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -91,3 +91,27 @@ after the function, not the interrupt / exception. Example below: ``` console $ cargo run --example binds {{#include ../../../../ci/expected/binds.run}}``` + +## Indirection for faster message passing + +Message passing always involves copying the payload from the sender into a +static variable and then from the static variable into the receiver. Thus +sending a large buffer, like a `[u8; 128]`, as a message involves two expensive +`memcpy`s. To minimize the message passing overhead one can use indirection: +instead of sending the buffer by value, one can send an owning pointer into the +buffer. + +One can use a global allocator to achieve indirection (`alloc::Box`, +`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.34.0, +or one can use a statically allocated memory pool like [`heapless::Pool`]. + +[`heapless::Pool`]: https://docs.rs/heapless/0.4.3/heapless/pool/index.html + +Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. + +``` rust +{{#include ../../../../examples/pool.rs}} +``` +``` console +$ cargo run --example binds +{{#include ../../../../ci/expected/pool.run}}``` diff --git a/ci/expected/pool.run b/ci/expected/pool.run new file mode 100644 index 0000000000..040dcee888 --- /dev/null +++ b/ci/expected/pool.run @@ -0,0 +1,2 @@ +bar(0x2000008c) +foo(0x20000110) diff --git a/examples/pool.rs b/examples/pool.rs new file mode 100644 index 0000000000..719d5e5dc6 --- /dev/null +++ b/examples/pool.rs @@ -0,0 +1,67 @@ +//! examples/pool.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +extern crate panic_semihosting; + +use cortex_m_semihosting::{debug, hprintln}; +use heapless::{ + pool, + pool::singleton::{Box, Pool}, +}; +use lm3s6965::Interrupt; +use rtfm::app; + +// Declare a pool of 128-byte memory blocks +pool!(P: [u8; 128]); + +#[app(device = lm3s6965)] +const APP: () = { + #[init] + fn init() { + static mut MEMORY: [u8; 512] = [0; 512]; + + // Increase the capacity of the memory pool by ~4 + P::grow(MEMORY); + + rtfm::pend(Interrupt::I2C0); + } + + #[interrupt(priority = 2, spawn = [foo, bar])] + fn I2C0() { + // claim a memory block, leave it uninitialized and .. + let x = P::alloc().unwrap().freeze(); + + // .. send it to the `foo` task + spawn.foo(x).ok().unwrap(); + + // send another block to the task `bar` + spawn.bar(P::alloc().unwrap().freeze()).ok().unwrap(); + } + + #[task] + fn foo(x: Box

) { + hprintln!("foo({:?})", x.as_ptr()).unwrap(); + + // explicitly return the block to the pool + drop(x); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2)] + fn bar(x: Box

) { + hprintln!("bar({:?})", x.as_ptr()).unwrap(); + + // this is done automatically so we can omit the call to `drop` + // drop(x); + } + + extern "C" { + fn UART0(); + fn UART1(); + } +};