rtic/book/ru/src/by-example/tasks.md

117 lines
7 KiB
Markdown
Raw Normal View History

2019-02-08 21:18:51 +01:00
# Программные задачи
2021-04-04 07:15:13 +02:00
В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события,
RTIC также поддерживает *программные* задачи, которые могут порождаться
приложением из любого контекста выполнения.
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Программным задачам можно также назначать приоритет и, под капотом, они
диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные
прерывания, были указаны в аргументе `dispatchers` модуля `app`, если используются
программные задачи; часть из этих свободных прерываний будут использованы для
управления программными задачами. Преимущество программных задач над аппаратными
в том, что множество задач можно назначить на один обработчик прерывания.
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Программные задачи также определяются атрибутом `task`, но аргумент `binds` опускается.
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах.
Три программные задачи привязаны к 2-м обработчикам прерываний.
2019-02-08 21:18:51 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/task.rs}}
2019-02-08 21:18:51 +01:00
```
``` console
$ cargo run --example task
2021-04-04 07:15:13 +02:00
{{#include ../../../../ci/expected/task.run}}
```
2019-02-08 21:18:51 +01:00
## Передача сообщений
2021-04-04 07:15:13 +02:00
Другое преимущество программной задачи в том, что задачам можно передать сообщения
в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре
задачи-обработчика.
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Пример ниже демонстрирует три задачи, две из которых ожидают сообщение.
2019-02-08 21:18:51 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/message.rs}}
2019-02-08 21:18:51 +01:00
```
``` console
$ cargo run --example message
2021-04-04 07:15:13 +02:00
{{#include ../../../../ci/expected/message.run}}
```
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
## Вместимость
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
RTIC *не* производит никакого рода аллокаций памяти в куче.
Память, необходимая для размещения сообщения резервируется статически.
По-умолчанию фреймворк минимизирует выделение памяти программой таким образом,
что каждая задача имеет "вместимость" для сообщения равную 1:
это значит, что не более одного сообщения можно передать задаче перед тем, как
у нее появится возможность к запуску. Это значение по-умолчанию можно
изменить для каждой задачи, используя аргумент `capacity`.
Этот аргумент принимает положительное целое, которое определяет как много
сообщений буфер сообщений задачи может хранить.
2019-02-08 21:18:51 +01:00
2021-04-04 07:15:13 +02:00
Пример ниже устанавливает вместимость программной задачи `foo` равной 4.
Если вместимость не установить, второй вызов `spawn.foo` в `UART0` приведет к ошибке (панике).
2019-02-08 21:18:51 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/capacity.rs}}
2019-02-08 21:18:51 +01:00
```
``` console
$ cargo run --example capacity
2021-04-04 07:15:13 +02:00
{{#include ../../../../ci/expected/capacity.run}}
```
## Обработка ошибок
Интерфейс `spawn` возвращает вариант `Err`, если для размещения сообщения нет места.
В большинстве сценариев возникающие ошибки обрабатываются одним из двух способов:
- Паника, с помощью `unwrap`, `expect`, и т.п. Этот метод используется, чтобы обнаружить
ошибку программиста (например bug) выбора вместительности, которая оказалась недостаточна.
Когда эта паника встречается во время тестирования, выбирается большая вместительность,
и перекомпиляция программы может решить проблему, но иногда достаточно окунуться глубже
и провести анализ времени выполнения программы, чтобы выяснить, может ли платформа
обрабатывать пиковые нагрузки, или процессор необходимо заменить на более быстрый.
- Игнорирование результата. В программах реального времени, как и в обычных, может быть
нормальным иногда терять данные, или не получать ответ на некоторые события в пиковых ситуациях.
В таких сценариях может быть допустимо игнорирование ошибки вызова `spawn`.
Следует отметить, что повторная попытка вызова `spawn` обычно неверный подход, поскольку
такая операция на практике вероятно никогда не завершится успешно.
Так как у нас есть только переключения контекста на задачи с *более высоким* приоритетом,
повторение вызова `spawn` на задаче с низким приоритом никогда не позволит планировщику
вызвать задачу, что значит, что буфер никогда не будет очищен. Такая ситуация отражена в
следующем наброске:
``` rust
#[rtic::app(..)]
mod app {
#[init(spawn = [foo, bar])]
fn init(cx: init::Context) {
cx.spawn.foo().unwrap();
cx.spawn.bar().unwrap();
}
#[task(priority = 2, spawn = [bar])]
fn foo(cx: foo::Context) {
// ..
// программа зависнет здесь
while cx.spawn.bar(payload).is_err() {
// повтор попытки вызова spawn, если произошла ошибка
}
}
#[task(priority = 1)]
fn bar(cx: bar::Context, payload: i32) {
// ..
}
}
```