diff options
Diffstat (limited to 'book/en/src/by-example')
| -rw-r--r-- | book/en/src/by-example/app.md | 17 | ||||
| -rw-r--r-- | book/en/src/by-example/resources.md | 70 | ||||
| -rw-r--r-- | book/en/src/by-example/tasks.md | 6 | ||||
| -rw-r--r-- | book/en/src/by-example/timer-queue.md | 4 | ||||
| -rw-r--r-- | book/en/src/by-example/types-send-sync.md | 27 |
5 files changed, 31 insertions, 93 deletions
diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 2c70af0..6a01193 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -34,14 +34,13 @@ And optionally, device specific peripherals through the `core` and `device` fiel of `init::Context`. `static mut` variables declared at the beginning of `init` will be transformed -into `&'static mut` references that are safe to access. +into `&'static mut` references that are safe to access. Notice, this feature may be deprecated in next release, see `task_local` resources. [`rtic::Peripherals`]: ../../api/rtic/struct.Peripherals.html The example below shows the types of the `core`, `device` and `cs` fields, and showcases safe access to a `static mut` variable. The `device` field is only -available when the `peripherals` argument is set to `true` (it defaults to -`false`). +available when the `peripherals` argument is set to `true` (default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. ``` rust {{#include ../../../../examples/init.rs}} @@ -71,12 +70,12 @@ then sends the microcontroller to sleep after running `init`. [SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit Like in `init`, `static mut` variables will be transformed into `&'static mut` -references that are safe to access. +references that are safe to access. Notice, this feature may be deprecated in the next release, see `task_local` resources. The example below shows that `idle` runs after `init`. -**Note:** The `loop {}` in idle cannot be empty as this will crash the microcontroller due to a bug -in LLVM which miss-optimizes empty loops to a `UDF` instruction in release mode. +**Note:** The `loop {}` in idle cannot be empty as this will crash the microcontroller due to +LLVM compiling empty loops to an `UDF` instruction in release mode. To avoid UB, the loop needs to imply a "side-effect" by inserting an assembly instruction (e.g., `WFI`) or a `continue`. ``` rust {{#include ../../../../examples/idle.rs}} @@ -146,9 +145,9 @@ $ cargo run --example preempt ``` Note that the task `gpiob` does *not* preempt task `gpioc` because its priority -is the *same* as `gpioc`'s. However, once `gpioc` terminates the execution of -task, `gpiob` is prioritized over `gpioa` due to its higher priority. `gpioa` -is resumed only after `gpiob` terminates. +is the *same* as `gpioc`'s. However, once `gpioc` returns, the execution of +task `gpiob` is prioritized over `gpioa` due to its higher priority. `gpioa` +is resumed only after `gpiob` returns. 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 diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 9d90fbe..91d143e 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -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 `Context` structure. -The example application shown below contains two interrupt handlers that share -access to a resource named `shared`. +The example application shown below contains two interrupt handlers that share access to a resource named `shared`. ``` rust {{#include ../../../../examples/resource.rs}} @@ -34,38 +33,26 @@ $ cargo run --example resource {{#include ../../../../ci/expected/resource.run}} ``` -Note that the `shared` resource cannot be accessed from `idle`. Attempting to do -so results in a compile error. +Note that the `shared` resource cannot be accessed from `idle`. Attempting to do so results in a compile error. ## `lock` -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. +Critical sections are required to access shared mutable data in a data race-free manner. -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. +The `resources` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task. + +The only method on this trait, [`lock`], runs its closure argument in a critical section. [`Mutex`]: ../../../api/rtic/trait.Mutex.html [`lock`]: ../../../api/rtic/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]. +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], and complies with [Stack Resource Policy(SRP)][srp] based scheduling of RTIC. [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 -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 +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 lowest priority handler. ``` rust @@ -79,19 +66,11 @@ $ cargo run --example lock ## Late resources -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. +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. -Late resources are useful for *moving* (as in transferring the ownership of) -peripherals initialized in `init` into interrupt handlers. +Late resources are useful e.g., to *move* (as in transferring the ownership of) peripherals initialized in `init` into tasks. -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. +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. [`Queue`]: ../../../api/heapless/spsc/struct.Queue.html @@ -106,27 +85,14 @@ $ cargo run --example late ## 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 +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. -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. +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. -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. +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/only-shared-access.rs}} diff --git a/book/en/src/by-example/tasks.md b/book/en/src/by-example/tasks.md index ba16404..8558a54 100644 --- a/book/en/src/by-example/tasks.md +++ b/book/en/src/by-example/tasks.md @@ -6,15 +6,13 @@ application from any execution context. Software tasks can also be assigned priorities and, under the hood, are dispatched from interrupt handlers. RTIC requires that free interrupts are -declared in an `extern` block when using software tasks; some of these free +declared in the `dispatchers` app argument when using software tasks; some of these free interrupts will be used to dispatch the software tasks. An advantage of software tasks over hardware tasks is that many tasks can be mapped to a single interrupt handler. Software tasks are also declared using the `task` attribute but the `binds` -argument must be omitted. To be able to spawn a software task from a context -the name of the task must appear in the `spawn` argument of the context -attribute (`init`, `idle`, `task`, etc.). +argument must be omitted. The example below showcases three software tasks that run at 2 different priorities. The three software tasks are mapped to 2 interrupts handlers. diff --git a/book/en/src/by-example/timer-queue.md b/book/en/src/by-example/timer-queue.md index 482aebc..2964175 100644 --- a/book/en/src/by-example/timer-queue.md +++ b/book/en/src/by-example/timer-queue.md @@ -29,9 +29,7 @@ on the built-in CYCle CouNTer (CYCCNT). Note that this is a 32-bit timer clocked at the frequency of the CPU and as such it is not suitable for tracking time spans in the order of seconds. -To be able to schedule a software task from a context the name of the task must -first appear in the `schedule` argument of the context attribute. When -scheduling a task the (user-defined) `Instant` at which the task should be +When scheduling a task the (user-defined) `Instant` at which the task should be executed must be passed as the first argument of the `schedule` invocation. Additionally, the chosen `monotonic` timer must be configured and initialized diff --git a/book/en/src/by-example/types-send-sync.md b/book/en/src/by-example/types-send-sync.md index 9cdb889..a45f179 100644 --- a/book/en/src/by-example/types-send-sync.md +++ b/book/en/src/by-example/types-send-sync.md @@ -27,31 +27,8 @@ resources. [`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html The `app` attribute will enforce that `Send` is implemented where required so -you don't need to worry much about it. It's more important to know where you do -*not* need the `Send` trait: on types that are transferred between tasks that -run at the *same* priority. This occurs in two places: in message passing and in -shared resources. - -The example below shows where a type that doesn't implement `Send` can be used. - -``` rust -{{#include ../../../../examples/not-send.rs}} -``` - -It's important to note that late initialization of resources is effectively a -send operation where the initial value is sent from the background context, -which has the lowest priority of `0`, to a task, which will run at a priority -greater than or equal to `1`. Thus all late resources need to implement the -`Send` trait, except for those exclusively accessed by `idle`, which runs at a -priority of `0`. - -Sharing a resource with `init` can be used to implement late initialization, see -example below. For that reason, resources shared with `init` must also implement -the `Send` trait. - -``` rust -{{#include ../../../../examples/shared-with-init.rs}} -``` +you don't need to worry much about it. Currently all types that are passed need +to be `Send` in RTIC, however this restriction might be relaxed in the future. ## `Sync` |
