Выбрать главу

// последнему элементу очереди)

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

let priority = Celclass="underline" :new(PRIORITY);

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

}

Task::bar => {

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

}

}

}

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

basepri::write(snapshot);

}

}

}

INPUTS плюс FQ, список свободной памяти равняется эффективному пулу памяти. Однако, вместо того список свободной памяти (связный список), чтобы отслеживать пустые ячейки в буфере INPUTS, мы используем SPSC очередь; это позволяет нам уменьшить количество критических секций. На самом деле благодаря этому выбору код диспетчера задач неблокируемый.

Фреймворк RTIC использует несколько очередей, такие как очереди готовности и списки свободной памяти. Когда список свободной памяти пуст, попытка выызова (spawn) задачи приводит к ошибке; это условие проверяется во время выполнения. Не все операции, произвожимые фреймворком с этими очередями проверяют их пустоту / наличие места. Например, возвращение ячейки списка свободной памяти (см. диспетчер задач) не проверяется, поскольку есть фиксированное количество таких ячеек циркулирующих в системе, равное вместительности списка свободной памяти. Аналогично, добавление записи в очередь готовности (см. Spawn) не проверяется, потому что вместительность очереди выбрана фреймворком.

Пользователи могут задавать вместительность программных задач; эта вместительность - максимальное количество сообщений, которые можно послать указанной задаче от задачи более высоким приоритетом до того, как spawn вернет ошибку. Эта определяемая пользователем иместительность - размер списка свободной памяти задачи (например foo_FQ), а также размер массива, содержащего входные данные для задачи (например foo_INPUTS).

Вместительность очереди готовности (например RQ1) вычисляется как сумма вместительностей всех задач, управляемх диспетчером; эта сумма является также количеством сообщений, которые очередь может хранить в худшем сценарии, когда все возможные сообщения были посланы до того, как диспетчер задач получает шанс на запуск. По этой причине получение ячейки списка свободной памяти при любой операции spawn приводит к тому, что очередь готовности еще не заполнена, поэтому вставка записи в список готовности может пропустить проверку "полна ли очередь?".

В нашем запущенном примере задача bar не принимает входных данных, поэтому мы можем пропустить проверку как bar_INPUTS, так и bar_FQ и позволить пользователю посылать неограниченное число сообщений задаче, но если бы мы сделали это, было бы невозможно превысить вместительность для RQ1, что позволяет нам пропустить проверку "полна ли очередь?" при вызове задачи baz. В разделе о очереди таймера мы увидим как список свободной памяти используется для задач без входных данных.

Очереди, использемые внутри интерфейса spawn, рассматриваются как обычные ресурсы и для них тоже работает анализ приоритетов. Важно заметить, что это SPSC очереди, и только один из конечных элементов становится ресурсом; другим конечным элементом владеет диспетчер задач.

Рассмотрим следующий пример:

#![allow(unused)]

fn main() {

#[rtic::app(device = ..)]

mod app {

#[idle(spawn = [foo, bar])]

fn idle(c: idle::Context) -> ! {

// ..

}

#[task]

fn foo(c: foo::Context) {

// ..

}

#[task]

fn bar(c: bar::Context) {

// ..

}

#[task(priority = 2, spawn = [foo])]

fn baz(c: baz::Context) {

// ..

}

#[task(priority = 3, spawn = [bar])]

fn quux(c: quux::Context) {

// ..

}

}

}

Вот как будет проходить анализ приоритетов:

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

   • idle (prio = 0) и quux (prio = 3) соревнуются за конечный потребитель bar_FQ; это приводит к максимальному приоритету 3.

   • idle (prio = 0), baz (prio = 2) и quux (prio = 3) соревнуются за конечный производитель RQ1; это приводит к максимальному приоритету 3