mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
Merge #410
410: resources r=AfoHT a=perlindgren resources Co-authored-by: Per Lindgren <per.lindgren@ltu.se>
This commit is contained in:
commit
445730756e
3 changed files with 22 additions and 54 deletions
|
@ -22,8 +22,7 @@ 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
|
resources are made available to the context under the `resources` field of the
|
||||||
`Context` structure.
|
`Context` structure.
|
||||||
|
|
||||||
The example application shown below contains two interrupt handlers that share
|
The example application shown below contains two interrupt handlers that share access to a resource named `shared`.
|
||||||
access to a resource named `shared`.
|
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/resource.rs}}
|
{{#include ../../../../examples/resource.rs}}
|
||||||
|
@ -34,38 +33,26 @@ $ cargo run --example resource
|
||||||
{{#include ../../../../ci/expected/resource.run}}
|
{{#include ../../../../ci/expected/resource.run}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the `shared` resource cannot be accessed from `idle`. Attempting to do
|
Note that the `shared` resource cannot be accessed from `idle`. Attempting to do so results in a compile error.
|
||||||
so results in a compile error.
|
|
||||||
|
|
||||||
## `lock`
|
## `lock`
|
||||||
|
|
||||||
In the presence of preemption critical sections are required to mutate shared
|
Critical sections are required to access shared mutable data in a data race-free manner.
|
||||||
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.
|
|
||||||
|
|
||||||
Where a critical section is required the framework hands out a resource proxy
|
The `resources` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task.
|
||||||
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
|
The only method on this trait, [`lock`], runs its closure argument in a critical section.
|
||||||
argument in a critical section.
|
|
||||||
|
|
||||||
[`Mutex`]: ../../../api/rtic/trait.Mutex.html
|
[`Mutex`]: ../../../api/rtic/trait.Mutex.html
|
||||||
[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock
|
[`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock
|
||||||
|
|
||||||
The critical section created by the `lock` API is based on dynamic priorities:
|
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
|
||||||
it temporarily raises the dynamic priority of the context to a *ceiling*
|
(ICPP)][icpp], and complies with [Stack Resource Policy(SRP)][srp] based scheduling of RTIC.
|
||||||
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
|
[icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol
|
||||||
|
[srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy
|
||||||
|
|
||||||
In the example below we have three interrupt handlers with priorities ranging
|
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 `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do nat access the `shared` resource, is free to preempt the critical section created by the
|
||||||
from one to three. The two handlers with the lower priorities contend for the
|
|
||||||
`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.
|
lowest priority handler.
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
|
@ -79,19 +66,11 @@ $ cargo run --example lock
|
||||||
|
|
||||||
## Late resources
|
## Late resources
|
||||||
|
|
||||||
Late resources are resources that are not given an initial value at compile time
|
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 `init::LateResources` values returned by the `init` function.
|
||||||
using the `#[init]` attribute but instead are initialized at runtime using the
|
|
||||||
`init::LateResources` values returned by the `init` function.
|
|
||||||
|
|
||||||
Late resources are useful for *moving* (as in transferring the ownership of)
|
Late resources are useful e.g., to *move* (as in transferring the ownership of) peripherals initialized in `init` into tasks.
|
||||||
peripherals initialized in `init` into interrupt handlers.
|
|
||||||
|
|
||||||
The example below uses late resources to establish a lockless, one-way channel
|
The example below uses late resources to establish a lockless, one-way channel between the `UART0` interrupt handler and the `idle` task. A single producer 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.
|
||||||
between the `UART0` interrupt handler and the `idle` task. A single producer
|
|
||||||
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.
|
|
||||||
|
|
||||||
[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html
|
[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html
|
||||||
|
|
||||||
|
@ -106,27 +85,14 @@ $ cargo run --example late
|
||||||
|
|
||||||
## Only shared access
|
## Only shared access
|
||||||
|
|
||||||
By default the framework assumes that all tasks require exclusive 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.
|
||||||
(`&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
|
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
|
||||||
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.
|
appropriate locking or atomic operations of its own.
|
||||||
|
|
||||||
Note that in this release of RTIC it is not possible to request both exclusive
|
Note that in this release of RTIC it is not possible to request both exclusive access (`&mut-`) and shared access (`&-`) to the *same* resource from different tasks. Attempting to do so will result in a compile error.
|
||||||
access (`&mut-`) and shared access (`&-`) to the *same* resource from different
|
|
||||||
tasks. Attempting to do so will result in a compile error.
|
|
||||||
|
|
||||||
In the example below a key (e.g. a cryptographic key) is loaded (or created) at
|
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.
|
||||||
runtime and then used from two tasks that run at different priorities without
|
|
||||||
any kind of lock.
|
|
||||||
|
|
||||||
``` rust
|
``` rust
|
||||||
{{#include ../../../../examples/only-shared-access.rs}}
|
{{#include ../../../../examples/only-shared-access.rs}}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
UART0: shared = 1
|
UART1: shared = 1
|
||||||
UART1: shared = 2
|
UART0: shared = 2
|
||||||
|
|
|
@ -41,6 +41,7 @@ mod app {
|
||||||
}
|
}
|
||||||
|
|
||||||
// `shared` can be accessed from this context
|
// `shared` can be accessed from this context
|
||||||
|
// defaults to priority 1
|
||||||
#[task(binds = UART0, resources = [shared])]
|
#[task(binds = UART0, resources = [shared])]
|
||||||
fn uart0(mut cx: uart0::Context) {
|
fn uart0(mut cx: uart0::Context) {
|
||||||
let shared = cx.resources.shared.lock(|shared| {
|
let shared = cx.resources.shared.lock(|shared| {
|
||||||
|
@ -52,7 +53,8 @@ mod app {
|
||||||
}
|
}
|
||||||
|
|
||||||
// `shared` can be accessed from this context
|
// `shared` can be accessed from this context
|
||||||
#[task(binds = UART1, resources = [shared])]
|
// explicitly set to priority 2
|
||||||
|
#[task(binds = UART1, resources = [shared], priority = 2)]
|
||||||
fn uart1(mut cx: uart1::Context) {
|
fn uart1(mut cx: uart1::Context) {
|
||||||
let shared = cx.resources.shared.lock(|shared| {
|
let shared = cx.resources.shared.lock(|shared| {
|
||||||
*shared += 1;
|
*shared += 1;
|
||||||
|
|
Loading…
Reference in a new issue