fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
foo::spawn().unwrap();
(Shared {}, Local {}, init::Monotonics())
}
#[task]
fn foo(_: foo::Context) {
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").unwrap();
// spawns `baz` onto the task scheduler
// `baz` has higher priority than `foo` so it immediately preempts `foo`
baz::spawn().unwrap();
hprintln!("foo - end").unwrap();
}
#[task]
fn bar(_: bar::Context) {
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[task(priority = 2)]
fn baz(_: baz::Context) {
hprintln!("baz").unwrap();
}
}
}
$ cargo run --example task
foo - start
foo - middle
baz
foo - end
bar
Другое преимущество программной задачи в том, что задачам можно передать сообщения в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре задачи-обработчика.
Пример ниже демонстрирует три задачи, две из которых ожидают сообщение.
#![allow(unused)]
fn main() {
//! examples/message.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
mod app {
use cortex_m_semihosting::{debug, hprintln};
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
foo::spawn(/* no message */).unwrap();
(Shared {}, Local {}, init::Monotonics())
}
#[task(local = [count: u32 = 0])]
fn foo(cx: foo::Context) {
hprintln!("foo").unwrap();
bar::spawn(*cx.local.count).unwrap();
*cx.local.count += 1;
}
#[task]
fn bar(_: bar::Context, x: u32) {
hprintln!("bar({})", x).unwrap();
baz::spawn(x + 1, x + 2).unwrap();
}
#[task]
fn baz(_: baz::Context, x: u32, y: u32) {
hprintln!("baz({}, {})", x, y).unwrap();
if x + y > 4 {
debug::exit(debug::EXIT_SUCCESS);
}
foo::spawn().unwrap();
}
}
}
$ cargo run --example message
foo
bar(0)
baz(1, 2)
foo
bar(1)
baz(2, 3)
RTIC не производит никакого рода аллокаций памяти в куче. Память, необходимая для размещения сообщения резервируется статически. По-умолчанию фреймворк минимизирует выделение памяти программой таким образом, что каждая задача имеет "вместимость" для сообщения равную 1: это значит, что не более одного сообщения можно передать задаче перед тем, как у нее появится возможность к запуску. Это значение по-умолчанию можно изменить для каждой задачи, используя аргумент capacity. Этот аргумент принимает положительное целое, которое определяет как много сообщений буфер сообщений задачи может хранить.
Пример ниже устанавливает вместимость программной задачи foo равной 4. Если вместимость не установить, второй вызов spawn.foo в UART0 приведет к ошибке (панике).
#![allow(unused)]
fn main() {
//! examples/capacity.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0])]
mod app {
use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt;
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
rtic::pend(Interrupt::UART0);
(Shared {}, Local {}, init::Monotonics())
}
#[task(binds = UART0)]
fn uart0(_: uart0::Context) {
foo::spawn(0).unwrap();
foo::spawn(1).unwrap();
foo::spawn(2).unwrap();
foo::spawn(3).unwrap();
bar::spawn().unwrap();
}
#[task(capacity = 4)]
fn foo(_: foo::Context, x: u32) {
hprintln!("foo({})", x).unwrap();
}
#[task]
fn bar(_: bar::Context) {
hprintln!("bar").unwrap();
debug::exit(debug::EXIT_SUCCESS);