aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/by-example/tasks.md
diff options
context:
space:
mode:
Diffstat (limited to 'book/en/src/by-example/tasks.md')
-rw-r--r--book/en/src/by-example/tasks.md96
1 files changed, 75 insertions, 21 deletions
diff --git a/book/en/src/by-example/tasks.md b/book/en/src/by-example/tasks.md
index edcdbed..1c9302f 100644
--- a/book/en/src/by-example/tasks.md
+++ b/book/en/src/by-example/tasks.md
@@ -1,22 +1,23 @@
# Software tasks
-RTFM treats interrupt and exception handlers as *hardware* tasks. Hardware tasks
-are invoked by the hardware in response to events, like pressing a button. RTFM
-also supports *software* tasks which can be spawned by the software from any
-execution context.
-
-Software tasks can also be assigned priorities and are dispatched from interrupt
-handlers. RTFM requires that free interrupts are declared in an `extern` block
-when using software tasks; 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 declared by applying the `task` attribute to functions. To be
-able to spawn a software task the name of the task must appear in the `spawn`
-argument of the context attribute (`init`, `idle`, `interrupt`, etc.).
+In addition to hardware tasks, which are invoked by the hardware in response to
+hardware events, RTFM also supports *software* tasks which can be spawned by the
+application from any execution context.
+
+Software tasks can also be assigned priorities and, under the hood, are
+dispatched from interrupt handlers. RTFM requires that free interrupts are
+declared in an `extern` block 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.).
The example below showcases three software tasks that run at 2 different
-priorities. The three tasks map to 2 interrupts handlers.
+priorities. The three software tasks are mapped to 2 interrupts handlers.
``` rust
{{#include ../../../../examples/task.rs}}
@@ -44,15 +45,17 @@ $ cargo run --example message
## Capacity
-Task dispatchers do *not* use any dynamic memory allocation. The memory required
-to store messages is statically reserved. The framework will reserve enough
-space for every context to be able to spawn each task at most once. This is a
-sensible default but the "inbox" capacity of each task can be controlled using
-the `capacity` argument of the `task` attribute.
+RTFM does *not* perform any form of heap-based memory allocation. The memory
+required to store messages is statically reserved. By default the framework
+minimizes the memory footprint of the application so each task has a message
+"capacity" of 1: meaning that at most one message can be posted to the task
+before it gets a chance to run. This default can be overridden for each task
+using the `capacity` argument. This argument takes a positive integer that
+indicates how many messages the task message buffer can hold.
The example below sets the capacity of the software task `foo` to 4. If the
capacity is not specified then the second `spawn.foo` call in `UART0` would
-fail.
+fail (panic).
``` rust
{{#include ../../../../examples/capacity.rs}}
@@ -61,3 +64,54 @@ fail.
``` console
$ cargo run --example capacity
{{#include ../../../../ci/expected/capacity.run}}```
+
+## Error handling
+
+The `spawn` API returns the `Err` variant when there's no space to send the
+message. In most scenarios spawning errors are handled in one of two ways:
+
+- Panicking, using `unwrap`, `expect`, etc. This approach is used to catch the
+ programmer error (i.e. bug) of selecting a capacity that was too small. When
+ this panic is encountered during testing choosing a bigger capacity and
+ recompiling the program may fix the issue but sometimes it's necessary to dig
+ deeper and perform a timing analysis of the application to check if the
+ platform can deal with peak payload or if the processor needs to be replaced
+ with a faster one.
+
+- Ignoring the result. In soft real time and non real time applications it may
+ be OK to occasionally lose data or fail to respond to some events during event
+ bursts. In those scenarios silently letting a `spawn` call fail may be
+ acceptable.
+
+It should be noted that retrying a `spawn` call is usually the wrong approach as
+this operation will likely never succeed in practice. Because there are only
+context switches towards *higher* priority tasks retrying the `spawn` call of a
+lower priority task will never let the scheduler dispatch said task meaning that
+its message buffer will never be emptied. This situation is depicted in the
+following snippet:
+
+``` rust
+#[rtfm::app(..)]
+const APP: () = {
+ #[init(spawn = [foo, bar])]
+ fn init(cx: init::Context) {
+ cx.spawn.foo().unwrap();
+ cx.spawn.bar().unwrap();
+ }
+
+ #[task(priority = 2, spawn = [bar])]
+ fn foo(cx: foo::Context) {
+ // ..
+
+ // the program will get stuck here
+ while cx.spawn.bar(payload).is_err() {
+ // retry the spawn call if it failed
+ }
+ }
+
+ #[task(priority = 1)]
+ fn bar(cx: bar::Context, payload: i32) {
+ // ..
+ }
+};
+```