aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/by-example/software_tasks.md
diff options
context:
space:
mode:
authorPer Lindgren <per.lindgren@ltu.se>2023-01-28 21:57:43 +0100
committerHenrik Tjäder <henrik@tjaders.com>2023-03-01 00:33:39 +0100
commit1f51b10297e9cbb4797aa1ed8be6a2b84c9f2e07 (patch)
treefaab2e5fd8a3432ac5b1f7be3bd9372d8063f8c5 /book/en/src/by-example/software_tasks.md
parentd0c51269608c18a105fd010f070bd9af6f443c60 (diff)
Book: Major rework for RTIC v2
Diffstat (limited to 'book/en/src/by-example/software_tasks.md')
-rw-r--r--book/en/src/by-example/software_tasks.md106
1 files changed, 79 insertions, 27 deletions
diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md
index 8ee185b..2752707 100644
--- a/book/en/src/by-example/software_tasks.md
+++ b/book/en/src/by-example/software_tasks.md
@@ -1,47 +1,99 @@
# Software tasks & spawn
-The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md)
-with the core difference that a software task is not explicitly bound to a specific
-interrupt vector, but rather bound to a “dispatcher” interrupt vector running
-at the intended priority of the software task (see below).
+The RTIC concept of a *software* task shares a lot with that of [hardware tasks](./hardware_tasks.md) with the core difference that a software task is not explicitly bound to a specific
+interrupt vector, but rather to a “dispatcher” interrupt vector running at the same priority as the software task.
-Thus, software tasks are tasks which are not *directly* bound to an interrupt vector.
+Similarly to *hardware* tasks, the `#[task]` attribute used on a function declare it as a task. The absence of a `binds = InterruptName` argument to the attribute declares the function as a *software task*.
-The `#[task]` attributes used on a function determine if it is
-software tasks, specifically the absence of a `binds = InterruptName`
-argument to the attribute definition.
+The static method `task_name::spawn()` spawns (starts) a software task and given that there are no higher priority tasks running the task will start executing directly.
-The static method `task_name::spawn()` spawns (schedules) a software
-task by registering it with a specific dispatcher. If there are no
-higher priority tasks available to the scheduler (which serves a set
-of dispatchers), the task will start executing directly.
+The *software* task itself is given as an `async` Rust function, which allows the user to optionally `await` future events. This allows to blend reactive programming (by means of *hardware* tasks) with sequential programming (by means of *software* tasks).
-All software tasks at the same priority level share an interrupt handler bound to their dispatcher.
-What differentiates software and hardware tasks is the usage of either a dispatcher or a bound interrupt vector.
+Whereas, *hardware* tasks are assumed to run-to-completion (and return), *software* tasks may be started (`spawned`) once and run forever, with the side condition that any loop (execution path) is broken by at least one `await` (yielding operation).
-The interrupt vectors used as dispatchers cannot be used by hardware tasks.
+All *software* tasks at the same priority level shares an interrupt handler acting as an async executor dispatching the software tasks.
-Availability of a set of “free” (not in use by hardware tasks) and usable interrupt vectors allows the framework
-to dispatch software tasks via dedicated interrupt handlers.
+This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute, where you define the set of free and usable interrupts.
-This set of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an
-argument to the `#[app]` attribute.
+Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that the list of dispatchers need to cover all priority levels used by software tasks.
-Each interrupt vector acting as dispatcher gets assigned to a unique priority level meaning that
-the list of dispatchers needs to cover all priority levels used by software tasks.
+Example: The `dispatchers =` argument needs to have at least 3 entries for an application using three different priorities for software tasks.
-Example: The `dispatchers =` argument needs to have at least 3 entries for an application using
-three different priorities for software tasks.
-
-The framework will give a compilation error if there are not enough dispatchers provided.
+The framework will give a compilation error if there are not enough dispatchers provided, or if a clash occurs between the list of dispatchers and interrupts bound to *hardware* tasks.
See the following example:
``` rust
-{{#include ../../../../examples/spawn.rs}}
+{{#include ../../../../rtic/examples/spawn.rs}}
```
``` console
$ cargo run --target thumbv7m-none-eabi --example spawn
-{{#include ../../../../ci/expected/spawn.run}}
+{{#include ../../../../rtic/ci/expected/spawn.run}}
+```
+You may `spawn` a *software* task again, given that it has run-to-completion (returned).
+
+In the below example, we `spawn` the *software* task `foo` from the `idle` task. Since the default priority of the *software* task is 1 (higher than `idle`), the dispatcher will execute `foo` (preempting `idle`). Since `foo` runs-to-completion. It is ok to `spawn` the `foo` task again.
+
+Technically the async executor will `poll` the `foo` *future* which in this case leaves the *future* in a *completed* state.
+
+``` rust
+{{#include ../../../../rtic/examples/spawn_loop.rs}}
+```
+
+``` console
+$ cargo run --target thumbv7m-none-eabi --example spawn_loop
+{{#include ../../../../rtic/ci/expected/spawn_loop.run}}
+```
+
+An attempt to `spawn` an already spawned task (running) task will result in an error. Notice, the that the error is reported before the `foo` task is actually run. This is since, the actual execution of the *software* task is handled by the dispatcher interrupt (`SSIO`), which is not enabled until we exit the `init` task. (Remember, `init` runs in a critical section, i.e. all interrupts being disabled.)
+
+Technically, a `spawn` to a *future* that is not in *completed* state is considered an error.
+
+``` rust
+{{#include ../../../../rtic/examples/spawn_err.rs}}
+```
+
+``` console
+$ cargo run --target thumbv7m-none-eabi --example spawn_err
+{{#include ../../../../rtic/ci/expected/spawn_err.run}}
+```
+
+## Passing arguments
+You can also pass arguments at spawn as follows.
+
+``` rust
+{{#include ../../../../rtic/examples/spawn_arguments.rs}}
```
+
+``` console
+$ cargo run --target thumbv7m-none-eabi --example spawn_arguments
+{{#include ../../../../rtic/ci/expected/spawn_arguments.run}}
+```
+
+## Priority zero tasks
+
+In RTIC tasks run preemptively to each other, with priority zero (0) the lowest priority. You can use priority zero tasks for background work, without any strict real-time requirements.
+
+Conceptually, one can see such tasks as running in the `main` thread of the application, thus the resources associated are not required the [Send] bound.
+
+[Send]: https://doc.rust-lang.org/nomicon/send-and-sync.html
+
+
+``` rust
+{{#include ../../../../rtic/examples/zero-prio-task.rs}}
+```
+
+``` console
+$ cargo run --target thumbv7m-none-eabi --example zero-prio-task
+{{#include ../../../../rtic/ci/expected/zero-prio-task.run}}
+```
+
+> **Notice**: *software* task at zero priority cannot co-exist with the [idle] task. The reason is that `idle` is running as a non-returning Rust function at priority zero. Thus there would be no way for an executor at priority zero to give control to *software* tasks at the same priority.
+
+---
+
+Application side safety: Technically, the RTIC framework ensures that `poll` is never executed on any *software* task with *completed* future, thus adhering to the soundness rules of async Rust.
+
+
+