rtic/book/en/src/by-example/resources.md

151 lines
6 KiB
Markdown
Raw Normal View History

2020-10-02 12:43:52 +02:00
# Resources
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
The framework provides an abstraction to share data between any of the contexts
we saw in the previous section (task handlers, `init` and `idle`): resources.
Resources are data visible only to functions declared within the `#[app]`
module. The framework gives the user complete control over which context
2019-08-21 10:17:27 +02:00
can access which resource.
All resources are declared as a single `struct` within the `#[app]`
module. Each field in the structure corresponds to a different resource.
The `struct` must be annotated with the following attribute: `#[resources]`.
2019-08-21 10:17:27 +02:00
Resources can optionally be given an initial value using the `#[init]`
attribute. Resources that are not given an initial value are referred to as
2020-06-11 15:45:53 +02:00
*late* resources and are covered in more detail in a follow-up section in this
2019-08-21 10:17:27 +02:00
page.
Each context (task handler, `init` or `idle`) must declare the resources it
intends to access in its corresponding metadata attribute using the `resources`
argument. This argument takes a list of resource names as its value. The listed
resources are made available to the context under the `resources` field of the
`Context` structure.
The example application shown below contains two interrupt handlers that share
access to a resource named `shared`.
2018-11-03 17:02:41 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/resource.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example resource
{{#include ../../../../ci/expected/resource.run}}
```
2018-11-03 17:02:41 +01:00
2020-06-11 15:45:53 +02:00
Note that the `shared` resource cannot be accessed from `idle`. Attempting to do
2019-08-21 10:17:27 +02:00
so results in a compile error.
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
## `lock`
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
In the presence of preemption critical sections are required to mutate shared
data in a data race free manner. As the framework has complete knowledge over
the priorities of tasks and which tasks can access which resources it enforces
that critical sections are used where required for memory safety.
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
Where a critical section is required the framework hands out a resource proxy
instead of a reference. This resource proxy is a structure that implements the
[`Mutex`] trait. The only method on this trait, [`lock`], runs its closure
argument in a critical section.
2018-11-03 17:02:41 +01:00
2020-06-11 19:18:29 +02:00
[`Mutex`]: ../../../api/rtic/trait.Mutex.html
[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock
2019-08-21 10:17:27 +02:00
The critical section created by the `lock` API is based on dynamic priorities:
it temporarily raises the dynamic priority of the context to a *ceiling*
priority that prevents other tasks from preempting the critical section. This
synchronization protocol is known as the [Immediate Ceiling Priority Protocol
(ICPP)][icpp].
[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol
2018-11-03 17:02:41 +01:00
In the example below we have three interrupt handlers with priorities ranging
from one to three. The two handlers with the lower priorities contend for the
2019-08-21 10:17:27 +02:00
`shared` resource. The lowest priority handler needs to `lock` the
`shared` resource to access its data, whereas the mid priority handler can
directly access its data. The highest priority handler, which cannot access
the `shared` resource, is free to preempt the critical section created by the
lowest priority handler.
2018-11-03 17:02:41 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/lock.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example lock
{{#include ../../../../ci/expected/lock.run}}
```
2018-11-03 17:02:41 +01:00
## Late resources
2020-06-11 15:45:53 +02:00
Late resources are resources that are not given an initial value at compile time
using the `#[init]` attribute but instead are initialized at runtime using the
2019-08-21 10:17:27 +02:00
`init::LateResources` values returned by the `init` function.
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
Late resources are useful for *moving* (as in transferring the ownership of)
peripherals initialized in `init` into interrupt handlers.
2018-11-03 17:02:41 +01:00
2020-06-11 15:45:53 +02:00
The example below uses late resources to establish a lockless, one-way channel
2019-08-21 10:17:27 +02:00
between the `UART0` interrupt handler and the `idle` task. A single producer
2018-11-03 17:02:41 +01:00
single consumer [`Queue`] is used as the channel. The queue is split into
consumer and producer end points in `init` and then each end point is stored
in a different resource; `UART0` owns the producer resource and `idle` owns
the consumer resource.
2019-09-17 19:55:55 +02:00
[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html
2018-11-03 17:02:41 +01:00
``` rust
2019-02-11 21:40:53 +01:00
{{#include ../../../../examples/late.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
$ cargo run --example late
{{#include ../../../../ci/expected/late.run}}
```
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
## Only shared access
By default the framework assumes that all tasks require exclusive access
(`&mut-`) to resources but it is possible to specify that a task only requires
shared access (`&-`) to a resource using the `&resource_name` syntax in the
`resources` list.
The advantage of specifying shared access (`&-`) to a resource is that no locks
are required to access the resource even if the resource is contended by several
tasks running at different priorities. The downside is that the task only gets a
shared reference (`&-`) to the resource, limiting the operations it can perform
on it, but where a shared reference is enough this approach reduces the number
of required locks. In addition to simple immutable data, this shared access can
be useful where the resource type safely implements interior mutability, with
appropriate locking or atomic operations of its own.
2018-11-03 17:02:41 +01:00
2020-06-11 19:18:29 +02:00
Note that in this release of RTIC it is not possible to request both exclusive
2019-08-21 10:17:27 +02:00
access (`&mut-`) and shared access (`&-`) to the *same* resource from different
tasks. Attempting to do so will result in a compile error.
2018-11-03 17:02:41 +01:00
2019-08-21 10:17:27 +02:00
In the example below a key (e.g. a cryptographic key) is loaded (or created) at
runtime and then used from two tasks that run at different priorities without
any kind of lock.
2018-11-03 17:02:41 +01:00
``` rust
2019-08-21 10:17:27 +02:00
{{#include ../../../../examples/only-shared-access.rs}}
2018-11-03 17:02:41 +01:00
```
``` console
2019-08-21 10:17:27 +02:00
$ cargo run --example only-shared-access
{{#include ../../../../ci/expected/only-shared-access.run}}
```
## Lock-free resource access of mutable resources
There exists two other options dealing with resources
* `#[lock_free]`: there might be several tasks with the same priority
accessing the resource without critical section. Since tasks with the
same priority never can preempt another task on the same priority
this is safe.
* `#[task_local]`: there must be only one task using this resource,
similar to a task local resource, but (optionally) set-up by init.