aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/by-example/resources.md
diff options
context:
space:
mode:
authorJorge Aparicio <jorge@japaric.io>2019-08-21 10:17:27 +0200
committerJorge Aparicio <jorge@japaric.io>2019-08-21 10:17:27 +0200
commit07b2b4d83078d0fd260d5f0812e8d5a34d02b793 (patch)
treedba2a8e8316e8cd868ccb7b46a80d63c5f61a224 /book/en/src/by-example/resources.md
parent0e146f8d1142672725b6abb38478f503a9261c80 (diff)
doc up
Diffstat (limited to 'book/en/src/by-example/resources.md')
-rw-r--r--book/en/src/by-example/resources.md146
1 files changed, 75 insertions, 71 deletions
diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md
index 06f2f06..e8f61d5 100644
--- a/book/en/src/by-example/resources.md
+++ b/book/en/src/by-example/resources.md
@@ -1,22 +1,27 @@
## Resources
-One of the limitations of the attributes provided by the `cortex-m-rt` crate is
-that sharing data (or peripherals) between interrupts, or between an interrupt
-and the `entry` function, requires a `cortex_m::interrupt::Mutex`, which
-*always* requires disabling *all* interrupts to access the data. Disabling all
-the interrupts is not always required for memory safety but the compiler doesn't
-have enough information to optimize the access to the shared data.
-
-The `app` attribute has a full view of the application thus it can optimize
-access to `static` variables. In RTFM we refer to the `static` variables
-declared inside the `app` pseudo-module as *resources*. To access a resource the
-context (`init`, `idle`, `interrupt` or `exception`) one must first declare the
-resource in the `resources` argument of its attribute.
-
-In the example below two interrupt handlers access the same resource. No `Mutex`
-is required in this case because the two handlers run at the same priority and
-no preemption is possible. The `SHARED` resource can only be accessed by these
-two handlers.
+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]`
+pseudo-module. The framework gives the user complete control over which context
+can access which resource.
+
+All resources are declared as a single `struct` within the `#[app]`
+pseudo-module. Each field in the structure corresponds to a different resource.
+Resources can optionally be given an initial value using the `#[init]`
+attribute. Resources that are not given an initial value are referred to as
+*late* resources and are covered in more detail in a follow up section in this
+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`.
``` rust
{{#include ../../../../examples/resource.rs}}
@@ -26,40 +31,39 @@ two handlers.
$ cargo run --example resource
{{#include ../../../../ci/expected/resource.run}}```
-## Priorities
+Note that the `shared` resource cannot accessed from `idle`. Attempting to do
+so results in a compile error.
-The priority of each handler can be declared in the `interrupt` and `exception`
-attributes. It's not possible to set the priority in any other way because the
-runtime takes ownership of the `NVIC` peripheral thus it's also not possible to
-change the priority of a handler / task at runtime. Thanks to this restriction
-the framework has knowledge about the *static* priorities of all interrupt and
-exception handlers.
+## `lock`
-Interrupts and exceptions can have priorities in the range `1..=(1 <<
-NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device`
-crate. The `idle` task has a priority of `0`, the lowest priority.
+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.
-Resources that are shared between handlers that run at different priorities
-require critical sections for memory safety. The framework ensures that critical
-sections are used but *only where required*: for example, no critical section is
-required by the highest priority handler that has access to the resource.
-
-The critical section API provided by the RTFM framework (see [`Mutex`]) is
-based on dynamic priorities rather than on disabling interrupts. The consequence
-is that these critical sections will prevent *some* handlers, including all the
-ones that contend for the resource, from *starting* but will let higher priority
-handlers, that don't contend for the resource, run.
+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.
[`Mutex`]: ../../api/rtfm/trait.Mutex.html
+[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
+
+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
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. 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 is free to preempt
-the critical section created by the lowest priority handler.
-
-[`lock`]: ../../api/rtfm/trait.Mutex.html#method.lock
+`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.
``` rust
{{#include ../../../../examples/lock.rs}}
@@ -69,27 +73,17 @@ the critical section created by the lowest priority handler.
$ cargo run --example lock
{{#include ../../../../ci/expected/lock.run}}```
-One more note about priorities: choosing a priority higher than what the device
-supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to
-limitations in the language the error message is currently far from helpful: it
-will say something along the lines of "evaluation of constant value failed" and
-the span of the error will *not* point out to the problematic interrupt value --
-we are sorry about this!
-
## Late resources
-Unlike normal `static` variables, which need to be assigned an initial value
-when declared, resources can be initialized at runtime. We refer to these
-runtime initialized resources as *late resources*. Late resources are useful for
-*moving* (as in transferring ownership) peripherals initialized in `init` into
-interrupt and exception handlers.
+Late resources are resources that are not given an initial value at compile
+using the `#[init]` attribute but instead are initialized are runtime using the
+`init::LateResources` values returned by the `init` function.
-Late resources are declared like normal resources but that are given an initial
-value of `()` (the unit value). `init` must return the initial values of all
-late resources packed in a `struct` of type `init::LateResources`.
+Late resources are useful for *moving* (as in transferring the ownership of)
+peripherals initialized in `init` into interrupt handlers.
The example below uses late resources to stablish a lockless, one-way channel
-between the `UART0` interrupt handler and the `idle` function. A single producer
+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
@@ -105,22 +99,32 @@ the consumer resource.
$ cargo run --example late
{{#include ../../../../ci/expected/late.run}}```
-## `static` resources
+## 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.
-`static` variables can also be used as resources. Tasks can only get `&`
-(shared) references to these resources but locks are never required to access
-their data. You can think of `static` resources as plain `static` variables that
-can be initialized at runtime and have better scoping rules: you can control
-which tasks can access the variable, instead of the variable being visible to
-all the functions in the scope it was declared in.
+Note that in this release of RTFM 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.
-In the example below a key is loaded (or created) at runtime and then used from
-two tasks that run at different priorities.
+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.
``` rust
-{{#include ../../../../examples/static.rs}}
+{{#include ../../../../examples/only-shared-access.rs}}
```
``` console
-$ cargo run --example static
-{{#include ../../../../ci/expected/static.run}}```
+$ cargo run --example only-shared-access
+{{#include ../../../../ci/expected/only-shared-access.run}}```