Типы, Send и Sync

Атрибут app вводит контекст, коллекцию переменных в каждую из функций. Все эти переменные имеют предсказуемые, неанонимные типы, поэтому Вы можете писать простые функции, получающие их как аргументы.

Описание API определяет как эти типы эти типы генерируются из входных данных. Вы можете также сгенерировать документацию для Вашей бинарной библиотеки (cargo doc --bin <name>); в документации Вы найдете структуры Context (например init::Context и idle::Context), чьи поля представляют переменные включенные в каждую функцию.

В примере ниже сгенерированы разные типы с помощью атрибута app.


# #![allow(unused_variables)]
#fn main() {
//! examples/types.rs

#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

use cortex_m_semihosting::debug;
use panic_semihosting as _;
use rtfm::cyccnt;

#[rtfm::app(device = lm3s6965, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
const APP: () = {
    struct Resources {
        #[init(0)]
        shared: u32,
    }

    #[init(schedule = [foo], spawn = [foo])]
    fn init(cx: init::Context) {
        let _: cyccnt::Instant = cx.start;
        let _: rtfm::Peripherals = cx.core;
        let _: lm3s6965::Peripherals = cx.device;
        let _: init::Schedule = cx.schedule;
        let _: init::Spawn = cx.spawn;

        debug::exit(debug::EXIT_SUCCESS);
    }

    #[idle(schedule = [foo], spawn = [foo])]
    fn idle(cx: idle::Context) -> ! {
        let _: idle::Schedule = cx.schedule;
        let _: idle::Spawn = cx.spawn;

        loop {}
    }

    #[task(binds = UART0, resources = [shared], schedule = [foo], spawn = [foo])]
    fn uart0(cx: uart0::Context) {
        let _: cyccnt::Instant = cx.start;
        let _: resources::shared = cx.resources.shared;
        let _: uart0::Schedule = cx.schedule;
        let _: uart0::Spawn = cx.spawn;
    }

    #[task(priority = 2, resources = [shared], schedule = [foo], spawn = [foo])]
    fn foo(cx: foo::Context) {
        let _: cyccnt::Instant = cx.scheduled;
        let _: &mut u32 = cx.resources.shared;
        let _: foo::Resources = cx.resources;
        let _: foo::Schedule = cx.schedule;
        let _: foo::Spawn = cx.spawn;
    }

    extern "C" {
        fn UART1();
    }
};

#}

Send

Send - маркерный типаж (trait) для "типов, которые можно передавать через границы потоков", как это определено в core. В контексте RTFM типаж Send необходим только там, где возможна передача значения между задачами, запускаемыми на разных приоритетах. Это возникает в нескольких случаях: при передаче сообщений, в совместно используемых static mut ресурсах и инициализации поздних ресурсов.

Атрибут app проверит, что Send реализован, где необходимо, поэтому Вам не стоит волноваться об этом. Более важно знать, где Вам не нужен типаж Send: в типах, передаваемых между задачами с одинаковым приоритетом. Это возникает в двух случаях: при передаче сообщений и в совместно используемых static mut ресурсах.

В примере ниже показано, где можно использовать типы, не реализующие Send.


# #![allow(unused_variables)]
#fn main() {
//! `examples/not-send.rs`

#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

use core::marker::PhantomData;

use cortex_m_semihosting::debug;
use panic_halt as _;
use rtfm::app;

pub struct NotSend {
    _0: PhantomData<*const ()>,
}

#[app(device = lm3s6965)]
const APP: () = {
    struct Resources {
        #[init(None)]
        shared: Option<NotSend>,
    }

    #[init(spawn = [baz, quux])]
    fn init(c: init::Context) {
        c.spawn.baz().unwrap();
        c.spawn.quux().unwrap();
    }

    #[task(spawn = [bar])]
    fn foo(c: foo::Context) {
        // scenario 1: message passed to task that runs at the same priority
        c.spawn.bar(NotSend { _0: PhantomData }).ok();
    }

    #[task]
    fn bar(_: bar::Context, _x: NotSend) {
        // scenario 1
    }

    #[task(priority = 2, resources = [shared])]
    fn baz(c: baz::Context) {
        // scenario 2: resource shared between tasks that run at the same priority
        *c.resources.shared = Some(NotSend { _0: PhantomData });
    }

    #[task(priority = 2, resources = [shared])]
    fn quux(c: quux::Context) {
        // scenario 2
        let _not_send = c.resources.shared.take().unwrap();

        debug::exit(debug::EXIT_SUCCESS);
    }

    extern "C" {
        fn UART0();
        fn UART1();
    }
};

#}

Sync

Похожая ситуация, Sync - маркерный типаж для "типов, на которых можно ссылаться в разных потоках", как это определено в core. В контексте RTFM типаж Sync необходим только там, где возможны две или более задачи, запускаемые на разных приоритетах, чтобы захватить разделяемую ссылку на ресурс. Это возникает только совместно используемых static-ресурсах.

Атрибут app проверит, что Sync реализован, где необходимо, но важно знать, где ограничение Sync не требуется: в static-ресурсах, разделяемых между задачами с одинаковым приоритетом.

В примере ниже показано, где можно использовать типы, не реализующие Sync.


# #![allow(unused_variables)]
#fn main() {
//! `examples/not-sync.rs`

#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]

use core::marker::PhantomData;

use cortex_m_semihosting::debug;
use panic_halt as _;

pub struct NotSync {
    _0: PhantomData<*const ()>,
}

#[rtfm::app(device = lm3s6965)]
const APP: () = {
    struct Resources {
        #[init(NotSync { _0: PhantomData })]
        shared: NotSync,
    }

    #[init]
    fn init(_: init::Context) {
        debug::exit(debug::EXIT_SUCCESS);
    }

    #[task(resources = [&shared])]
    fn foo(c: foo::Context) {
        let _: &NotSync = c.resources.shared;
    }

    #[task(resources = [&shared])]
    fn bar(c: bar::Context) {
        let _: &NotSync = c.resources.shared;
    }

    extern "C" {
        fn UART0();
    }
};

#}