aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/internals/late-resources.md
diff options
context:
space:
mode:
Diffstat (limited to 'book/en/src/internals/late-resources.md')
-rw-r--r--book/en/src/internals/late-resources.md116
1 files changed, 116 insertions, 0 deletions
diff --git a/book/en/src/internals/late-resources.md b/book/en/src/internals/late-resources.md
new file mode 100644
index 00000000..f3a0b0ae
--- /dev/null
+++ b/book/en/src/internals/late-resources.md
@@ -0,0 +1,116 @@
+# Late resources
+
+Some resources are initialized at runtime after the `init` function returns.
+It's important that these resources (static variables) are fully initialized
+before tasks are allowed to run, that is they must be initialized while
+interrupts are disabled.
+
+The example below shows the kind of code that the framework generates to
+initialize late resources.
+
+``` rust
+#[rtic::app(device = ..)]
+mod app {
+ struct Resources {
+ x: Thing,
+ }
+
+ #[init]
+ fn init() -> init::LateResources {
+ // ..
+
+ init::LateResources {
+ x: Thing::new(..),
+ }
+ }
+
+ #[task(binds = UART0, resources = [x])]
+ fn foo(c: foo::Context) {
+ let x: &mut Thing = c.resources.x;
+
+ x.frob();
+
+ // ..
+ }
+
+ // ..
+}
+```
+
+The code generated by the framework looks like this:
+
+``` rust
+fn init(c: init::Context) -> init::LateResources {
+ // .. user code ..
+}
+
+fn foo(c: foo::Context) {
+ // .. user code ..
+}
+
+// Public API
+pub mod init {
+ pub struct LateResources {
+ pub x: Thing,
+ }
+
+ // ..
+}
+
+pub mod foo {
+ pub struct Resources<'a> {
+ pub x: &'a mut Thing,
+ }
+
+ pub struct Context<'a> {
+ pub resources: Resources<'a>,
+ // ..
+ }
+}
+
+/// Implementation details
+mod app {
+ // uninitialized static
+ static mut x: MaybeUninit<Thing> = MaybeUninit::uninit();
+
+ #[no_mangle]
+ unsafe fn main() -> ! {
+ cortex_m::interrupt::disable();
+
+ // ..
+
+ let late = init(..);
+
+ // initialization of late resources
+ x.as_mut_ptr().write(late.x);
+
+ cortex_m::interrupt::enable(); //~ compiler fence
+
+ // exceptions, interrupts and tasks can preempt `main` at this point
+
+ idle(..)
+ }
+
+ #[no_mangle]
+ unsafe fn UART0() {
+ foo(foo::Context {
+ resources: foo::Resources {
+ // `x` has been initialized at this point
+ x: &mut *x.as_mut_ptr(),
+ },
+ // ..
+ })
+ }
+}
+```
+
+An important detail here is that `interrupt::enable` behaves like a *compiler
+fence*, which prevents the compiler from reordering the write to `X` to *after*
+`interrupt::enable`. If the compiler were to do that kind of reordering there
+would be a data race between that write and whatever operation `foo` performs on
+`X`.
+
+Architectures with more complex instruction pipelines may need a memory barrier
+(`atomic::fence`) instead of a compiler fence to fully flush the write operation
+before interrupts are re-enabled. The ARM Cortex-M architecture doesn't need a
+memory barrier in single-core context.