Real-Time Interrupt-driven Concurrency — страница 23 из 23

fn bar(c: bar::Context) {

// ..

}


#[task(priority = 1)]

fn baz(c: baz::Context) {

// ..

}

}

}

Анализ приоритетов происходил бы вот так:

   • foo (prio = 3) и baz (prio = 1) планируемые задачи, поэтому SysTick должен работать на максимальном из этих двух приоритетов, т.е. 3.

   • foo::Spawn (prio = 3) и bar::Schedule (prio = 2) соперничают за конечный потребитель baz_FQ; это приводит к максимальному приоритету 3.

   • bar::Schedule (prio = 2) имеет экслюзивный доступ к конечному потребителю foo_FQ; поэтому максимальный приоритет foo_FQ фактически 2.

   • SysTick (prio = 3) и bar::Schedule (prio = 2) соперничают за очередь таймера TQ; это приводит к максимальному приоритету 3.

   • SysTick (prio = 3) и foo::Spawn (prio = 3) оба имеют неблокируемый доступ к очереди готовности RQ3, что хранит записи foo; поэтому максимальный приоритет RQ3 фактически 3.

   • SysTick имеет эксклюзивный доступ к очереди готовности RQ1, которая хранит записи baz; поэтому максимальный приоритет RQ1 фактически 3.

Изменения в реализации
spawn

Когда интерфейс schedule используется, реализация spawn немного изменяется, чтобы отслеживать baseline задач. Как можете видеть в реализации schedule есть буферы INSTANTS, используемые, чтобы хранить время, в которое задача была запланирована навыполнение; этот Instant читается диспетчером задач и передается в пользовательский код, как часть контекста задачи.


#![allow(unused)]

fn main() {

mod app {

// ..


#[no_mangle]

unsafe UART1() {

const PRIORITY: u8 = 1;


let snapshot = basepri::read();


while let Some(ready) = RQ1.split().1.dequeue() {

match ready.task {

Task::baz => {

let input = baz_INPUTS[ready.index as usize].read();

// ADDED

let instant = baz_INSTANTS[ready.index as usize].read();


baz_FQ.split().0.enqueue_unchecked(ready.index);


let priority = Cell::new(PRIORITY);

// ИЗМЕНЕНО instant передан как часть контекста задачи

baz(baz::Context::new(&priority, instant), input)

}


Task::bar => {

// выглядит также как ветка для `baz`

}


}

}


// инвариант BASEPRI

basepri::write(snapshot);

}

}

}

И наоборот, реализации spawn нужно писать значение в буфер INSTANTS. Записанное значение располагается в структуре Spawn и это либо время start аппаратной задачи, либо время scheduled программной задачи.


#![allow(unused)]

fn main() {

mod foo {

// ..


pub struct Spawn<'a> {

priority: &'a Cell,

// ADDED

instant: Instant,

}


impl<'a> Spawn<'a> {

pub unsafe fn priority(&self) ->&Cell {

&self.priority

}


// ADDED

pub unsafe fn instant(&self) -> Instant {

self.instant

}

}

}


mod app {

impl<'a> foo::Spawn<'a> {

/// Spawns the `baz` task

pub fn baz(&self, message: u64) -> Result<(), u64> {

unsafe {

match lock(self.priority(), baz_FQ_CEILING, || {

baz_FQ.split().1.dequeue()

}) {

Some(index) => {

baz_INPUTS[index as usize].write(message);

// ADDED

baz_INSTANTS[index as usize].write(self.instant());


lock(self.priority(), RQ1_CEILING, || {

RQ1.split().1.enqueue_unchecked(Ready {

task: Task::foo,

index,

});

});


rtic::pend(Interrupt::UART0);

}


None => {

// достигнута максимальная вместительность; неудачный вызов

Err(message)

}

}

}

}

}

}

}