aboutsummaryrefslogtreecommitdiff
path: root/book/ru/src/by-example/tasks.md
blob: 3c99d00efca1dc137450374b590f4ef22081100c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Программные задачи

В дополнение к аппаратным задачам, вызываемым в ответ на аппаратные события,
RTIC также поддерживает *программные* задачи, которые могут порождаться
приложением из любого контекста выполнения.

Программным задачам можно также назначать приоритет и, под капотом, они
диспетчеризуются обработчиками прерываний. RTIC требует, чтобы свободные
прерывания, были указаны в аргументе `dispatchers` модуля `app`, если используются
программные задачи; часть из этих свободных прерываний будут использованы для
управления программными задачами. Преимущество программных задач над аппаратными
в том, что множество задач можно назначить на один обработчик прерывания.

Программные задачи также определяются атрибутом `task`, но аргумент `binds` опускается.

Пример ниже демонстрирует три программные задачи, запускаемых 2-х разных приоритетах.
Три программные задачи привязаны к 2-м обработчикам прерываний.

``` rust
{{#include ../../../../examples/task.rs}}
```

``` console
$ cargo run --example task
{{#include ../../../../ci/expected/task.run}}
```

## Передача сообщений

Другое преимущество программной задачи в том, что задачам можно передать сообщения
в момент их запуска. Тип передаваемого сообщения должен быть определен в сигнатуре
задачи-обработчика.

Пример ниже демонстрирует три задачи, две из которых ожидают сообщение.

``` rust
{{#include ../../../../examples/message.rs}}
```

``` console
$ cargo run --example message
{{#include ../../../../ci/expected/message.run}}
```

## Вместимость

RTIC *не* производит никакого рода аллокаций памяти в куче.
Память, необходимая для размещения сообщения резервируется статически.
По-умолчанию фреймворк минимизирует выделение памяти программой таким образом,
что каждая задача имеет "вместимость" для сообщения равную 1:
это значит, что не более одного сообщения можно передать задаче перед тем, как
у нее появится возможность к запуску. Это значение по-умолчанию можно
изменить для каждой задачи, используя аргумент `capacity`.
Этот аргумент принимает положительное целое, которое определяет как много
сообщений буфер сообщений задачи может хранить.

Пример ниже устанавливает вместимость программной задачи `foo` равной 4.
Если вместимость не установить, второй вызов `spawn.foo` в `UART0` приведет к ошибке (панике).

``` rust
{{#include ../../../../examples/capacity.rs}}
```

``` console
$ cargo run --example capacity
{{#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) {
        // ..
    }
}
```