diff --git a/examples/minimal_early_resource.rs b/examples/minimal_early_resource.rs new file mode 100644 index 0000000000..f6b6a1a5a8 --- /dev/null +++ b/examples/minimal_early_resource.rs @@ -0,0 +1,25 @@ +//! examples/idle.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + + #[resources] + struct Resources { + #[init(0)] + resource_x: u32, + } + + #[idle(resources = [resource_x])] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } +} diff --git a/examples/minimal_late_resource.rs b/examples/minimal_late_resource.rs new file mode 100644 index 0000000000..2419b8d7d2 --- /dev/null +++ b/examples/minimal_late_resource.rs @@ -0,0 +1,29 @@ +//! examples/minimal_late_resource.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + + #[resources] + struct Resources { + resource_x: u32, + } + + #[init] + fn init(_: init::Context) -> (init::LateResources, init::Monotonics) { + (init::LateResources { resource_x: 0 }, init::Monotonics {}) + } + + #[idle(resources = [resource_x])] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } +} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 96c5df80f9..eec633ba2b 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -17,10 +17,14 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // If it's live let cfgs = app.late_resources[name].cfgs.clone(); if analysis.locations.get(name).is_some() { - // Need to also include the cfgs stmts.push(quote!( - #(#cfgs)* - #mangled_name.as_mut_ptr().write(late.#name); + // We include the cfgs + #(#cfgs)* + // Late resource is a RacyCell> + // - `get_mut_unchecked` to obtain `MaybeUninit` + // - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit` + // - `write` the defined value for the late resource T + #mangled_name.get_mut_unchecked().as_mut_ptr().write(late.#name); )); } } diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs index fa52b86db6..89f70f8402 100644 --- a/macros/src/codegen/resources.rs +++ b/macros/src/codegen/resources.rs @@ -4,13 +4,20 @@ use rtic_syntax::{analyze::Ownership, ast::App}; use crate::{analyze::Analysis, check::Extra, codegen::util}; -/// Generates `static [mut]` variables and resource proxies +/// Generates `static` variables and resource proxies +/// Early resources are stored in `RacyCell` +/// Late resource are stored in `RacyCell>` +/// +/// Safety: +/// - RacyCell access is `unsafe`. +/// - RacyCell is always written to before user access, thus +// the generated code for user access can safely `assume_init`. pub fn codegen( app: &App, analysis: &Analysis, extra: &Extra, ) -> ( - // mod_app -- the `static [mut]` variables behind the proxies + // mod_app -- the `static` variables behind the proxies Vec, // mod_resources -- the `resources` module TokenStream2, @@ -24,18 +31,26 @@ pub fn codegen( let mangled_name = util::mark_internal_ident(&name); { + // TODO: do we really need this in the single core case + // late resources in `util::link_section_uninit` let section = if expr.is_none() { util::link_section_uninit(true) } else { None }; + // resource type and assigned value let (ty, expr) = if let Some(expr) = expr { - (quote!(#ty), quote!(#expr)) - } else { + // early resource ( - quote!(core::mem::MaybeUninit<#ty>), - quote!(core::mem::MaybeUninit::uninit()), + quote!(rtic::RacyCell<#ty>), + quote!(rtic::RacyCell::new(#expr)), + ) + } else { + // late resource + ( + quote!(rtic::RacyCell>), + quote!(rtic::RacyCell::new(core::mem::MaybeUninit::uninit())), ) }; @@ -46,7 +61,7 @@ pub fn codegen( #(#attrs)* #(#cfgs)* #section - static mut #mangled_name: #ty = #expr; + static #mangled_name: #ty = #expr; )); } @@ -74,14 +89,16 @@ pub fn codegen( )); let ptr = if expr.is_none() { + // late resource quote!( #(#cfgs)* - #mangled_name.as_mut_ptr() + &mut #mangled_name.get_mut_unchecked().assume_init() ) } else { + // early resource quote!( #(#cfgs)* - &mut #mangled_name + unsafe { #mangled_name.get_mut_unchecked() } ) }; diff --git a/src/lib.rs b/src/lib.rs index 822073999f..2da2ec9583 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,3 +55,24 @@ where { NVIC::pend(interrupt) } + +use core::cell::UnsafeCell; + +/// Internal replacement for `static mut T` +// TODO: Decide name and location. +pub struct RacyCell(UnsafeCell); + +impl RacyCell { + /// Create a RacyCell + pub const fn new(value: T) -> Self { + RacyCell(UnsafeCell::new(value)) + } + + /// Get &mut T + pub unsafe fn get_mut_unchecked(&self) -> &mut T { + &mut *self.0.get() + } +} + +// The type wrapped need to be Sync for RacyCell to be Sync +unsafe impl Sync for RacyCell where T: Sync {}