aboutsummaryrefslogtreecommitdiff
path: root/book/en/src/internals/late-resources.md
diff options
context:
space:
mode:
authorJorge Aparicio <jorge@japaric.io>2019-04-21 20:45:24 +0200
committerJorge Aparicio <jorge@japaric.io>2019-05-01 22:36:54 +0200
commit09ec5a7a41d951ad7a1ef61391896df4a1f5fc18 (patch)
tree0ab3172a9f2fd42d7236b4e989726ce312e363ce /book/en/src/internals/late-resources.md
parentbc024f197929be1ce7dac9e6cbf6672c3980437e (diff)
document internals
note that this assumes that RFC #155 has been implemented
Diffstat (limited to 'book/en/src/internals/late-resources.md')
-rw-r--r--book/en/src/internals/late-resources.md115
1 files changed, 115 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 0000000..71157f2
--- /dev/null
+++ b/book/en/src/internals/late-resources.md
@@ -0,0 +1,115 @@
+# 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
+#[rtfm::app(device = ..)]
+const APP: () = {
+ // late resource
+ static mut 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
+const 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.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.