diff options
| author | Jorge Aparicio <jorge@japaric.io> | 2019-04-21 20:45:24 +0200 |
|---|---|---|
| committer | Jorge Aparicio <jorge@japaric.io> | 2019-05-01 22:36:54 +0200 |
| commit | 09ec5a7a41d951ad7a1ef61391896df4a1f5fc18 (patch) | |
| tree | 0ab3172a9f2fd42d7236b4e989726ce312e363ce /book/en/src/internals/late-resources.md | |
| parent | bc024f197929be1ce7dac9e6cbf6672c3980437e (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.md | 115 |
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. |
