diff options
Diffstat (limited to 'book/ru/src/by-example/tasks.md')
| -rw-r--r-- | book/ru/src/by-example/tasks.md | 113 |
1 files changed, 83 insertions, 30 deletions
diff --git a/book/ru/src/by-example/tasks.md b/book/ru/src/by-example/tasks.md index 3782804..3c99d00 100644 --- a/book/ru/src/by-example/tasks.md +++ b/book/ru/src/by-example/tasks.md @@ -1,22 +1,20 @@ # Программные задачи -RTIC обрабатывает прерывания и исключения как *аппаратные* задачи. Аппаратные -задачи могут вызываться устройством в ответ на события, такие как нажатие кнопки. -RTIC также поддерживает *программные* задачи, порождаемые программой из любого -контекста выполнения. +В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события, +RTIC также поддерживает *программные* задачи, которые могут порождаться +приложением из любого контекста выполнения. -Программным задачам также можно назначать приоритет и диспетчеризовать из -обработчиков прерываний. RTIC требует определения свободных прерываний в блоке -`extern`, когда используются программные задачи; эти свободные прерывания будут использованы, чтобы диспетчеризовать программные задачи. Преимущество программных -задач перед аппаратными в том, что на один обработчик прерывания можно назначить -множество задач. +Программным задачам можно также назначать приоритет и, под капотом, они +диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные +прерывания, были указаны в аргументе `dispatchers` модуля `app`, если используются +программные задачи; часть из этих свободных прерываний будут использованы для +управления программными задачами. Преимущество программных задач над аппаратными +в том, что множество задач можно назначить на один обработчик прерывания. -Программные задачи определяются заданием функциям атрибута `task`. Чтобы было -возможно вызывать программные задачи, имя задачи нужно передать в аргументе -`spawn` контекста атрибута (`init`, `idle`, `interrupt`, etc.). +Программные задачи также определяются атрибутом `task`, но аргумент `binds` опускается. -В примере ниже продемонстрированы три программных задачи, запускаемые на 2-х -разных приоритетах. Трем задачам назначены 2 обработчика прерываний. +Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах. +Три программные задачи привязаны к 2-м обработчикам прерываний. ``` rust {{#include ../../../../examples/task.rs}} @@ -24,15 +22,16 @@ RTIC также поддерживает *программные* задачи, ``` console $ cargo run --example task -{{#include ../../../../ci/expected/task.run}}``` +{{#include ../../../../ci/expected/task.run}} +``` ## Передача сообщений -Другое преимущество программных задач - возможность передавать сообщения задачам -во время их вызова. Тип полезной нагрузки сообщения должен быть определен в -сигнатуре обработчика задачи. +Другое преимущество программной задачи в том, что задачам можно передать сообщения +в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре +задачи-обработчика. -Пример ниже демонстрирует три задачи, две из которых ожидают сообщения. +Пример ниже демонстрирует три задачи, две из которых ожидают сообщение. ``` rust {{#include ../../../../examples/message.rs}} @@ -40,19 +39,23 @@ $ cargo run --example task ``` console $ cargo run --example message -{{#include ../../../../ci/expected/message.run}}``` +{{#include ../../../../ci/expected/message.run}} +``` -## Ёмкость +## Вместимость -Диспетчеры задач *не* используют динамическое выделение памяти. Память -необходимая для размещения сообщений, резервируется статически. Фреймворк -зарезервирует достаточно памяти для каждого контекста, чтобы можно было вызвать -каждую задачу как минимум единожды. Это разумно по умолчанию, но -"внутреннюю" ёмкость каждой задачи можно контролировать используя аргумент -`capacity` атрибута `task`. +RTIC *не* производит никакого рода аллокаций памяти в куче. +Память, необходимая для размещения сообщения резервируется статически. +По-умолчанию фреймворк минимизирует выделение памяти программой таким образом, +что каждая задача имеет "вместимость" для сообщения равную 1: +это значит, что не более одного сообщения можно передать задаче перед тем, как +у нее появится возможность к запуску. Это значение по-умолчанию можно +изменить для каждой задачи, используя аргумент `capacity`. +Этот аргумент принимает положительное целое, которое определяет как много +сообщений буфер сообщений задачи может хранить. -В примере ниже установлена ёмкость программной задачи `foo` на 4. Если ёмкость -не определена, тогда второй вызов `spawn.foo` в `UART0` вызовет ошибку. +Пример ниже устанавливает вместимость программной задачи `foo` равной 4. +Если вместимость не установить, второй вызов `spawn.foo` в `UART0` приведет к ошибке (панике). ``` rust {{#include ../../../../examples/capacity.rs}} @@ -60,4 +63,54 @@ $ cargo run --example message ``` console $ cargo run --example capacity -{{#include ../../../../ci/expected/capacity.run}}``` +{{#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) { + // .. + } +} +``` |
