goodby static mut, welcome back to UnsafeCell

This commit is contained in:
Per Lindgren 2021-03-10 15:37:11 +01:00
parent 3c86d713a6
commit db1574bf6b
5 changed files with 108 additions and 12 deletions

View file

@ -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();
}
}
}

View file

@ -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();
}
}
}

View file

@ -17,10 +17,14 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// If it's live // If it's live
let cfgs = app.late_resources[name].cfgs.clone(); let cfgs = app.late_resources[name].cfgs.clone();
if analysis.locations.get(name).is_some() { if analysis.locations.get(name).is_some() {
// Need to also include the cfgs
stmts.push(quote!( stmts.push(quote!(
#(#cfgs)* // We include the cfgs
#mangled_name.as_mut_ptr().write(late.#name); #(#cfgs)*
// Late resource is a RacyCell<MaybeUninit<T>>
// - `get_mut_unchecked` to obtain `MaybeUninit<T>`
// - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit<T>`
// - `write` the defined value for the late resource T
#mangled_name.get_mut_unchecked().as_mut_ptr().write(late.#name);
)); ));
} }
} }

View file

@ -4,13 +4,20 @@ use rtic_syntax::{analyze::Ownership, ast::App};
use crate::{analyze::Analysis, check::Extra, codegen::util}; 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<T>`
/// Late resource are stored in `RacyCell<MaybeUninit<T>>`
///
/// Safety:
/// - RacyCell<T> access is `unsafe`.
/// - RacyCell<MaybeUninit> is always written to before user access, thus
// the generated code for user access can safely `assume_init`.
pub fn codegen( pub fn codegen(
app: &App, app: &App,
analysis: &Analysis, analysis: &Analysis,
extra: &Extra, extra: &Extra,
) -> ( ) -> (
// mod_app -- the `static [mut]` variables behind the proxies // mod_app -- the `static` variables behind the proxies
Vec<TokenStream2>, Vec<TokenStream2>,
// mod_resources -- the `resources` module // mod_resources -- the `resources` module
TokenStream2, TokenStream2,
@ -24,18 +31,26 @@ pub fn codegen(
let mangled_name = util::mark_internal_ident(&name); 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() { let section = if expr.is_none() {
util::link_section_uninit(true) util::link_section_uninit(true)
} else { } else {
None None
}; };
// resource type and assigned value
let (ty, expr) = if let Some(expr) = expr { let (ty, expr) = if let Some(expr) = expr {
(quote!(#ty), quote!(#expr)) // early resource
} else {
( (
quote!(core::mem::MaybeUninit<#ty>), quote!(rtic::RacyCell<#ty>),
quote!(core::mem::MaybeUninit::uninit()), quote!(rtic::RacyCell::new(#expr)),
)
} else {
// late resource
(
quote!(rtic::RacyCell<core::mem::MaybeUninit<#ty>>),
quote!(rtic::RacyCell::new(core::mem::MaybeUninit::uninit())),
) )
}; };
@ -46,7 +61,7 @@ pub fn codegen(
#(#attrs)* #(#attrs)*
#(#cfgs)* #(#cfgs)*
#section #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() { let ptr = if expr.is_none() {
// late resource
quote!( quote!(
#(#cfgs)* #(#cfgs)*
#mangled_name.as_mut_ptr() &mut #mangled_name.get_mut_unchecked().assume_init()
) )
} else { } else {
// early resource
quote!( quote!(
#(#cfgs)* #(#cfgs)*
&mut #mangled_name unsafe { #mangled_name.get_mut_unchecked() }
) )
}; };

View file

@ -55,3 +55,24 @@ where
{ {
NVIC::pend(interrupt) NVIC::pend(interrupt)
} }
use core::cell::UnsafeCell;
/// Internal replacement for `static mut T`
// TODO: Decide name and location.
pub struct RacyCell<T>(UnsafeCell<T>);
impl<T> RacyCell<T> {
/// 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<T> to be Sync
unsafe impl<T> Sync for RacyCell<T> where T: Sync {}