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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# Ресурсы
Фреймворк предоставляет абстракцию для разделения данных между любыми контекстами,
с которыми мы встречались в предыдущей главе (задачами-обработчиками, `init` и `idle`): ресурсы.
Ресурсы - это данные, видимые только функциями, определенными внутри модуля `#[app]`.
Фреймворк дает пользователю полный контроль за тем, какой контекст может
получить доступ к какому ресурсу.
Все ресурсы определены в *двух* структурах внутри модуля `#[app]`.
Каждое поле этих структур соответствует отдельному ресурсу.
Одна `struct`-ура должна быть аннотирована атрибутом `#[local]`.
Другая `struct`-ура должна быть аннотирована атрибутом `#[shared]`.
Разница между этими двумя множествами ресурсов будет описана познее.
Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым
он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя
либо аргумент `local`, либо `shared`. Этот аргумент принимает список имен ресурсов в качестве значения.
Перечисленные ресурсы становятся доступны в контексте через поля `local` и `shared` структуры `Context`.
Во время выполнения при выходе из функции `#[init]` все ресурсы инициализированы.
Функция `#[init]` должна возвращать начальные значения для всех ресурсов;
отсюда следует, что тип возвращаемого ею значения включает типы
структур `#[shared]` и `#[local]`.
Поскольку ресурсы инициализированы в ходе функции `#[init]`, к ним нельзя
получить доступ внетри функции `#[init]`.
Пример программы, показанной ниже содержит два обработчика прерывания.
Каждый обработчик имеет доступ к его собственному `#[local]` ресурсу.
``` rust
{{#include ../../../../examples/resource.rs}}
```
``` console
$ cargo run --example resource
{{#include ../../../../ci/expected/resource.run}}
```
К ресурсу `#[local]` нельзя получить доступ извне задачи к которой он
привязан атрибутом `#[task]`.
Попытка обращения к одному и тому же ресурсу `#[local]` из более чем одной
задачи - ошибка компиляции.
## `lock`
Критические секции необходимы для доступа к ресурсам `#[shared]` таким образом,
чтобы избежать гонок данных.
Поле `shared`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого
ресурса, доступного задаче.
Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции.
[`Mutex`]: ../../../api/rtic/trait.Mutex.html
[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock
Критическая секция, создаваемая интерфейсом `lock` основана на динамических приоритетах:
она временно повышает динамический приоритет контекста до *максимального* приоритета,
что не дает другим задачам возможности вытеснить критическую секцию.
Этот протокол синхронизации известен как [Протокол немедленного максимального приоритета
(ICPP)][icpp], и компилируется диспетчером RTIC с [Политикой ресурсов стека(SRP)][srp].
[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol
[srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy
В примере ниже у нас есть три обработчика прерываний с приоритетами от одного до трех.
Два из обработчиков с более низким приоритетом соревнуются за ресурс `shared`,
поэтому должны блокировать доступа к данным ресурса.
Обработчик с наивысшим приоритетом, который не имеет доступа к ресурсу `shared`,
может свободно вытеснять критическую секцию, созданную обработчиком с низким приоритетом.
``` rust
{{#include ../../../../examples/lock.rs}}
```
``` console
$ cargo run --example lock
{{#include ../../../../ci/expected/lock.run}}
```
## Множественное блокировка
Это расширение к `lock`, чтобы уменьшить количесво отступов, блокируемые ресурсы можно объединять в кортежи.
Следующий пример это демонстрирует:
``` rust
{{#include ../../../../examples/multilock.rs}}
```
## Только разделяемый (`&-`) доступ
По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам,
но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса
`&resource_name` в списке `resources`.
Преимущество указания разделяемого досупа (`&-`) к ресурсу в том, что для доступа к ресурсу
не нужна блокировка, даже если за ресурс соревнуются несколько задач, запускаемые с
разными приоритетами. Недостаток в том, что задача получает только разделяемую ссылку (`&-`)
на ресурс, и ограничена операциями, возможными с ней, но там, где разделяемой ссылки достаточно,
такой подход уменьшает количесво требуемых блокировок.
В дополнение к простым неизменяемым данным, такой разделяемый доступ может быть полезен для
ресурсов, безопасно реализующих внутреннюю мутабельность с самоблокировкой или атомарными операциями.
Заметьте, что в этом релизе RTIC невозможно запросить и эксклюзивный доступ (`&mut-`)
и разделяемый (`&-`) для *одного и того же* ресурса из различных задач.
Попытка это сделать приведет к ошибке компиляции.
В примере ниже ключ (например криптографический ключ) загружается (или создается) во время выполнения,
а затем используется двумя задачами, запускаемымы с различным приоритетом без каких-либо блокировок.
``` rust
{{#include ../../../../examples/only-shared-access.rs}}
```
``` console
$ cargo run --example only-shared-access
{{#include ../../../../ci/expected/only-shared-access.run}}
```
## Неблокируемый доступ к изменяемым ресурсам
Критическая секция *не* требуется для доступа к ресурсу `#[shared]`,
к которому обращаются только из задач с *одинаковым* приоритетом.
В этом случае вы можете избежать `lock` API, добавив атрибут поля `#[lock_free]` при объявдении ресурса (смотреть пример ниже).
Заметьте, что это лишь для удобства: даже если вы используете `lock` API,
во время выполнения фреймворк *не* создаст критическую секцию.
Еще одно ценное замечание: использование `#[lock_free]` на ресурсах,
разделяемых задачами, запускаемыми с разными приоритетами
приведет к ошибке *компиляции* -- не импользование `lock` API может
привести к гонке данных в этом случае.
``` rust
{{#include ../../../../examples/lock-free.rs}}
```
``` console
$ cargo run --example lock-free
{{#include ../../../../ci/expected/lock-free.run}}
```
|