diff options
Diffstat (limited to 'book/en/src/internals/late-resources.md')
-rw-r--r-- | book/en/src/internals/late-resources.md | 116 |
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. |