Одна из основ RTIC - контроль доступа. Контроль того, какая часть программы может получить доступ к какой статической переменной - инструмент обеспечения безопасности памяти.
Статические переменные используются для разделения состояний между обработчиками прерываний, или между обработчиком прерывания и нижним контекстом выполнения, main. В обычном Rust коде трудно обеспечить гранулированный контроль за тем, какие функции могут получать доступ к статическим переменным, поскольку к статическим переменным можно получить доступ из любой функции, находящейся в той же области видимости, в которой они определены. Модули дают частичный контроль над доступом к статическим переменным, но они недостаточно гибкие.
Чтобы добиться полного контроля за тем, что задачи могут получить доступ только к статическим переменным (ресурсам), которые им были указаны в RTIC атрибуте, фреймворк RTIC производит трансформацию структуры кода. Эта трансформация состоит из размещения ресурсов (статических переменных), определенных пользователем внутри модуля, а пользовательского кода вне модуля. Это делает невозможным обращение пользовательского кода к статическим переменным.
Затем доступ к ресурсам предоставляется каждой задаче с помощью структуры Resources, чьи поля соответствуют ресурсам, к которым получает доступ задача. Есть лишь одна такая структура на задачу и структура Resources инициализируется либо уникальной ссылкой (&mut-) на статическую переменную, либо с помощью прокси-ресурса (см. раздел критические секции).
Код ниже - пример разных трансформаций структуры кода, происходящих за сценой:
#![allow(unused)]
fn main() {
#[rtic::app(device = ..)]
mod app {
static mut X: u64: 0;
static mut Y: bool: 0;
#[init(resources = [Y])]
fn init(c: init::Context) {
// .. пользовательский код ..
}
#[interrupt(binds = UART0, resources = [X])]
fn foo(c: foo::Context) {
// .. пользовательский код ..
}
#[interrupt(binds = UART1, resources = [X, Y])]
fn bar(c: bar::Context) {
// .. пользовательский код ..
}
// ..
}
}
Фреймворк создает код, подобный этому:
fn init(c: init::Context) {
// .. пользовательский код ..
}
fn foo(c: foo::Context) {
// .. пользовательский код ..
}
fn bar(c: bar::Context) {
// .. пользовательский код ..
}
// Публичное API
pub mod init {
pub struct Context<'a> {
pub resources: Resources<'a>,
// ..
}
pub struct Resources<'a> {
pub Y: &'a mut bool,
}
}
pub mod foo {
pub struct Context<'a> {
pub resources: Resources<'a>,
// ..
}
pub struct Resources<'a> {
pub X: &'a mut u64,
}
}
pub mod bar {
pub struct Context<'a> {
pub resources: Resources<'a>,
// ..
}
pub struct Resources<'a> {
pub X: &'a mut u64,
pub Y: &'a mut bool,
}
}
/// Детали реализации
mod app {
// все, что внутри этого модуля спрятано от пользовательского кода
static mut X: u64 = 0;
static mut Y: bool = 0;
// настоящая точка входа в программу
unsafe fn main() -> ! {
interrupt::disable();
// ..
// вызов пользовательского кода; передача ссылок на статические переменные
init(init::Context {
resources: init::Resources {
X: &mut X,
},
// ..
});
// ..
interrupt::enable();
// ..
}
// обработчик прерывания,с которым связан `foo`
#[no_mangle]
unsafe fn UART0() {
// вызов пользовательского кода; передача ссылок на статические переменные
foo(foo::Context {
resources: foo::Resources {
X: &mut X,
},
// ..
});
}
// обработчик прерывания,с которым связан `bar`
#[no_mangle]
unsafe fn UART1() {
// вызов пользовательского кода; передача ссылок на статические переменные
bar(bar::Context {
resources: bar::Resources {
X: &mut X,
Y: &mut Y,
},
// ..
});
}
}