По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (&mut-) к ресурсам, но возможно указать, что задаче достаточен разделяемый доступ (&-) к ресурсы с помощью синтакисиса &resource_name в списке resources.
Преимущество указания разделяемого досупа (&-) к ресурсу в том, что для доступа к ресурсу не нужна блокировка, даже если за ресурс соревнуются несколько задач, запускаемые с разными приоритетами. Недостаток в том, что задача получает только разделяемую ссылку (&-) на ресурс, и ограничена операциями, возможными с ней, но там, где разделяемой ссылки достаточно, такой подход уменьшает количесво требуемых блокировок. В дополнение к простым неизменяемым данным, такой разделяемый доступ может быть полезен для ресурсов, безопасно реализующих внутреннюю мутабельность с самоблокировкой или атомарными операциями.
Заметьте, что в этом релизе RTIC невозможно запросить и эксклюзивный доступ (&mut-) и разделяемый (&-) для одного и того же ресурса из различных задач. Попытка это сделать приведет к ошибке компиляции.
В примере ниже ключ (например криптографический ключ) загружается (или создается) во время выполнения, а затем используется двумя задачами, запускаемымы с различным приоритетом без каких-либо блокировок.
#![allow(unused)]
fn main() {
//! examples/static.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 {
key: u32,
}
#[local]
struct Local {}
#[init]
fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {
rtic::pend(Interrupt::UART0);
rtic::pend(Interrupt::UART1);
(Shared { key: 0xdeadbeef }, Local {}, init::Monotonics())
}
#[task(binds = UART0, shared = [&key])]
fn uart0(cx: uart0::Context) {
let key: &u32 = cx.shared.key;
hprintln!("UART0(key = {:#x})", key).unwrap();
debug::exit(debug::EXIT_SUCCESS);
}
#[task(binds = UART1, priority = 2, shared = [&key])]
fn uart1(cx: uart1::Context) {
hprintln!("UART1(key = {:#x})", cx.shared.key).unwrap();
}
}
}
$ cargo run --example only-shared-access
UART1(key = 0xdeadbeef)
UART0(key = 0xdeadbeef)
Есть две других возможности доступа к ресурсам
• #[lock_free]: могут быть несколько задач с одинаковым приоритетом, получающие доступ к ресурсу без критических секций. Так как задачи с одинаковым приоритетом никогда не могут вытеснить друг друга, это безопасно.
• #[task_local]: в этом случае должна быть только одна задача, использующая этот ресурс, так же как локальный static mut ресурс задачи, но (опционально) устанавливаемая с в init.
В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события, RTIC также поддерживает программные задачи, которые могут порождаться приложением из любого контекста выполнения.
Программным задачам можно также назначать приоритет и, под капотом, они диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные прерывания, были указаны в аргументе dispatchers модуля app, если используются программные задачи; часть из этих свободных прерываний будут использованы для управления программными задачами. Преимущество программных задач над аппаратными в том, что множество задач можно назначить на один обработчик прерывания.
Программные задачи также определяются атрибутом task, но аргумент binds опускается.
Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах. Три программные задачи привязаны к 2-м обработчикам прерываний.
#![allow(unused)]
fn main() {
//! examples/task.rs
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_semihosting as _;
#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])]
mod app {
use cortex_m_semihosting::{debug, hprintln};
#[shared]
struct Shared {}
#[local]
struct Local {}
#[init]