Now all locks are symmetric

Test fixes

Fix test

Fix comment
This commit is contained in:
Emil Fresk 2020-10-22 21:36:32 +02:00
parent b3aa9e99a9
commit e8eca4be37
22 changed files with 183 additions and 223 deletions

View file

@ -252,7 +252,6 @@ jobs:
types types
not-sync not-sync
shared-with-init
generics generics
cfg cfg

View file

@ -0,0 +1,48 @@
//! examples/big-struct-opt.rs
//!
//! Example on how to initialize a large struct without needing to copy it via `LateResources`,
//! effectively saving stack space needed for the copies.
#![no_main]
#![no_std]
use panic_halt as _;
/// Some big struct
pub struct BigStruct {
/// Big content
pub data: [u8; 2048],
}
impl BigStruct {
fn new() -> Self {
BigStruct { data: [22; 2048] }
}
}
#[rtic::app(device = lm3s6965)]
mod app {
use super::BigStruct;
use core::mem::MaybeUninit;
#[resources]
struct Resources {
big_struct: &'static mut BigStruct,
}
#[init]
fn init(_: init::Context) -> init::LateResources {
let big_struct = unsafe {
static mut BIG_STRUCT: MaybeUninit<BigStruct> = MaybeUninit::uninit();
// write directly into the static storage
BIG_STRUCT.as_mut_ptr().write(BigStruct::new());
&mut *BIG_STRUCT.as_mut_ptr()
};
init::LateResources {
// assign the reference so we can use the resource
big_struct,
}
}
}

View file

@ -41,12 +41,12 @@ mod app {
} }
#[task(capacity = 2, resources = [count])] #[task(capacity = 2, resources = [count])]
fn foo(_cx: foo::Context) { fn foo(mut _cx: foo::Context) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
*_cx.resources.count += 1; _cx.resources.count.lock(|count| *count += 1);
log::spawn(*_cx.resources.count).unwrap(); log::spawn(_cx.resources.count.lock(|count| *count)).unwrap();
} }
// this wouldn't compile in `release` mode // this wouldn't compile in `release` mode
@ -59,12 +59,12 @@ mod app {
// currently still present in the Tasks enum // currently still present in the Tasks enum
#[cfg(never)] #[cfg(never)]
#[task(capacity = 2, resources = [count])] #[task(capacity = 2, resources = [count])]
fn foo2(_cx: foo2::Context) { fn foo2(mut _cx: foo2::Context) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
*_cx.resources.count += 10; _cx.resources.count.lock(|count| *count += 10);
log::spawn(*_cx.resources.count).unwrap(); log::spawn(_cx.resources.count.lock(|count| *count)).unwrap();
} }
// this wouldn't compile in `release` mode // this wouldn't compile in `release` mode

View file

@ -38,12 +38,12 @@ mod app {
} }
#[task(capacity = 2, resources = [count])] #[task(capacity = 2, resources = [count])]
fn foo(_cx: foo::Context) { fn foo(mut _cx: foo::Context) {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ {
*_cx.resources.count += 1; _cx.resources.count.lock(|count| *count += 1);
log::spawn(*_cx.resources.count).unwrap(); log::spawn(_cx.resources.count.lock(|count| *count)).unwrap();
} }
// this wouldn't compile in `release` mode // this wouldn't compile in `release` mode

View file

@ -32,7 +32,7 @@ mod app {
} }
// Direct destructure // Direct destructure
#[task(binds = UART0, resources = [a, b, c])] #[task(binds = UART0, resources = [&a, &b, &c])]
fn uart0(cx: uart0::Context) { fn uart0(cx: uart0::Context) {
let a = cx.resources.a; let a = cx.resources.a;
let b = cx.resources.b; let b = cx.resources.b;
@ -42,7 +42,7 @@ mod app {
} }
// De-structure-ing syntax // De-structure-ing syntax
#[task(binds = UART1, resources = [a, b, c])] #[task(binds = UART1, resources = [&a, &b, &c])]
fn uart1(cx: uart1::Context) { fn uart1(cx: uart1::Context) {
let uart1::Resources { a, b, c } = cx.resources; let uart1::Resources { a, b, c } = cx.resources;

View file

@ -13,7 +13,6 @@ use rtic::Mutex;
mod app { mod app {
use cortex_m_semihosting::{debug, hprintln}; use cortex_m_semihosting::{debug, hprintln};
use lm3s6965::Interrupt; use lm3s6965::Interrupt;
use rtic::Exclusive;
#[resources] #[resources]
struct Resources { struct Resources {
@ -49,11 +48,8 @@ mod app {
hprintln!("UART1(STATE = {})", *STATE).unwrap(); hprintln!("UART1(STATE = {})", *STATE).unwrap();
// just to show that `shared` can be accessed directly // second argument has type `resources::shared`
*c.resources.shared += 0; super::advance(STATE, c.resources.shared);
// second argument has type `Exclusive<u32>`
super::advance(STATE, Exclusive(c.resources.shared));
} }
} }

View file

@ -35,9 +35,9 @@ mod app {
} }
#[idle(resources = [c])] #[idle(resources = [c])]
fn idle(c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
loop { loop {
if let Some(byte) = c.resources.c.dequeue() { if let Some(byte) = c.resources.c.lock(|c| c.dequeue()) {
hprintln!("received message: {}", byte).unwrap(); hprintln!("received message: {}", byte).unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
@ -48,7 +48,7 @@ mod app {
} }
#[task(binds = UART0, resources = [p])] #[task(binds = UART0, resources = [p])]
fn uart0(c: uart0::Context) { fn uart0(mut c: uart0::Context) {
c.resources.p.enqueue(42).unwrap(); c.resources.p.lock(|p| p.enqueue(42).unwrap());
} }
} }

View file

@ -52,11 +52,15 @@ mod app {
} }
#[task(binds = GPIOB, priority = 2, resources = [shared])] #[task(binds = GPIOB, priority = 2, resources = [shared])]
fn gpiob(c: gpiob::Context) { fn gpiob(mut c: gpiob::Context) {
// the higher priority task does *not* need a critical section // the higher priority task does still need a critical section
*c.resources.shared += 1; let shared = c.resources.shared.lock(|shared| {
*shared += 1;
hprintln!("D - shared = {}", *c.resources.shared).unwrap(); *shared
});
hprintln!("D - shared = {}", shared).unwrap();
} }
#[task(binds = GPIOC, priority = 3)] #[task(binds = GPIOC, priority = 3)]

View file

@ -47,18 +47,23 @@ mod app {
// `shared` can be accessed from this context // `shared` can be accessed from this context
#[task(binds = UART0, resources = [shared])] #[task(binds = UART0, resources = [shared])]
fn uart0(cx: uart0::Context) { fn uart0(mut cx: uart0::Context) {
let shared: &mut u32 = cx.resources.shared; let shared = cx.resources.shared.lock(|shared| {
*shared += 1; *shared += 1;
*shared
});
hprintln!("UART0: shared = {}", shared).unwrap(); hprintln!("UART0: shared = {}", shared).unwrap();
} }
// `shared` can be accessed from this context // `shared` can be accessed from this context
#[task(binds = UART1, resources = [shared])] #[task(binds = UART1, resources = [shared])]
fn uart1(cx: uart1::Context) { fn uart1(mut cx: uart1::Context) {
*cx.resources.shared += 1; let shared = cx.resources.shared.lock(|shared| {
*shared += 1;
*shared
});
hprintln!("UART1: shared = {}", cx.resources.shared).unwrap(); hprintln!("UART1: shared = {}", shared).unwrap();
} }
} }

View file

@ -42,18 +42,23 @@ mod app {
// `shared` can be accessed from this context // `shared` can be accessed from this context
#[task(binds = UART0, resources = [shared])] #[task(binds = UART0, resources = [shared])]
fn uart0(cx: uart0::Context) { fn uart0(mut cx: uart0::Context) {
let shared: &mut u32 = cx.resources.shared; let shared = cx.resources.shared.lock(|shared| {
*shared += 1; *shared += 1;
*shared
});
hprintln!("UART0: shared = {}", shared).unwrap(); hprintln!("UART0: shared = {}", shared).unwrap();
} }
// `shared` can be accessed from this context // `shared` can be accessed from this context
#[task(binds = UART1, resources = [shared])] #[task(binds = UART1, resources = [shared])]
fn uart1(cx: uart1::Context) { fn uart1(mut cx: uart1::Context) {
*cx.resources.shared += 1; let shared = cx.resources.shared.lock(|shared| {
*shared += 1;
*shared
});
hprintln!("UART1: shared = {}", cx.resources.shared).unwrap(); hprintln!("UART1: shared = {}", shared).unwrap();
} }
} }

View file

@ -1,45 +0,0 @@
//! `examples/shared-with-init.rs`
#![deny(unsafe_code)]
#![deny(warnings)]
#![no_main]
#![no_std]
use panic_halt as _;
use rtic::app;
pub struct MustBeSend;
#[app(device = lm3s6965)]
mod app {
use super::MustBeSend;
use cortex_m_semihosting::debug;
use lm3s6965::Interrupt;
#[resources]
struct Resources {
#[init(None)]
shared: Option<MustBeSend>,
}
#[init(resources = [shared])]
fn init(c: init::Context) -> init::LateResources {
// this `message` will be sent to task `UART0`
let message = MustBeSend;
*c.resources.shared = Some(message);
rtic::pend(Interrupt::UART0);
init::LateResources {}
}
#[task(binds = UART0, resources = [shared])]
fn uart0(c: uart0::Context) {
if let Some(message) = c.resources.shared.take() {
// `message` has been received
drop(message);
debug::exit(debug::EXIT_SUCCESS);
}
}
}

View file

@ -36,9 +36,9 @@ mod app {
} }
#[idle(resources = [c])] #[idle(resources = [c])]
fn idle(c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
loop { loop {
if let Some(byte) = c.resources.c.dequeue() { if let Some(byte) = c.resources.c.lock(|c| c.dequeue()) {
hprintln!("received message: {}", byte).unwrap(); hprintln!("received message: {}", byte).unwrap();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
@ -49,9 +49,9 @@ mod app {
} }
#[task(binds = UART0, resources = [p])] #[task(binds = UART0, resources = [p])]
fn uart0(c: uart0::Context) { fn uart0(mut c: uart0::Context) {
static mut KALLE: u32 = 0; static mut KALLE: u32 = 0;
*KALLE += 1; *KALLE += 1;
c.resources.p.enqueue(42).unwrap(); c.resources.p.lock(|p| p.enqueue(42).unwrap());
} }
} }

View file

@ -23,11 +23,8 @@ mod app {
y: Option<NotSend>, y: Option<NotSend>,
} }
#[init(resources = [y])] #[init]
fn init(c: init::Context) -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
// equivalent to late resource initialization
*c.resources.y = Some(NotSend { _0: PhantomData });
init::LateResources { init::LateResources {
x: NotSend { _0: PhantomData }, x: NotSend { _0: PhantomData },
} }

View file

@ -31,29 +31,18 @@ mod app {
s3: u32, // idle & uart0 s3: u32, // idle & uart0
} }
#[init(resources = [o1, o4, o5, o6, s3])] #[init]
fn init(c: init::Context) -> init::LateResources { fn init(_: init::Context) -> init::LateResources {
// owned by `init` == `&'static mut`
let _: &'static mut u32 = c.resources.o1;
// owned by `init` == `&'static` if read-only
let _: &'static u32 = c.resources.o6;
// `init` has exclusive access to all resources
let _: &mut u32 = c.resources.o4;
let _: &mut u32 = c.resources.o5;
let _: &mut u32 = c.resources.s3;
init::LateResources {} init::LateResources {}
} }
#[idle(resources = [o2, &o4, s1, &s3])] #[idle(resources = [o2, &o4, s1, &s3])]
fn idle(mut c: idle::Context) -> ! { fn idle(mut c: idle::Context) -> ! {
// owned by `idle` == `&'static mut` // owned by `idle` == `&'static mut`
let _: &'static mut u32 = c.resources.o2; let _: resources::o2 = c.resources.o2;
// owned by `idle` == `&'static` if read-only // owned by `idle` == `&'static` if read-only
let _: &'static u32 = c.resources.o4; let _: &u32 = c.resources.o4;
// shared with `idle` == `Mutex` // shared with `idle` == `Mutex`
c.resources.s1.lock(|_| {}); c.resources.s1.lock(|_| {});
@ -69,13 +58,13 @@ mod app {
#[task(binds = UART0, resources = [o3, s1, s2, &s3])] #[task(binds = UART0, resources = [o3, s1, s2, &s3])]
fn uart0(c: uart0::Context) { fn uart0(c: uart0::Context) {
// owned by interrupt == `&mut` // owned by interrupt == `&mut`
let _: &mut u32 = c.resources.o3; let _: resources::o3 = c.resources.o3;
// no `Mutex` proxy when access from highest priority task // no `Mutex` proxy when access from highest priority task
let _: &mut u32 = c.resources.s1; let _: resources::s1 = c.resources.s1;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks // no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: &mut u32 = c.resources.s2; let _: resources::s2 = c.resources.s2;
// `&` if read-only // `&` if read-only
let _: &u32 = c.resources.s3; let _: &u32 = c.resources.s3;
@ -87,6 +76,6 @@ mod app {
let _: &u32 = c.resources.o5; let _: &u32 = c.resources.o5;
// no `Mutex` proxy when co-owned by cooperative (same priority) tasks // no `Mutex` proxy when co-owned by cooperative (same priority) tasks
let _: &mut u32 = c.resources.s2; let _: resources::s2 = c.resources.s2;
} }
} }

View file

@ -61,9 +61,11 @@ mod app {
// l2 ok (task_local) // l2 ok (task_local)
// e1 ok (lock_free) // e1 ok (lock_free)
#[task(priority = 1, binds = UART0, resources = [shared, l2, e1])] #[task(priority = 1, binds = UART0, resources = [shared, l2, e1])]
fn uart0(cx: uart0::Context) { fn uart0(mut cx: uart0::Context) {
let shared: &mut u32 = cx.resources.shared; let shared = cx.resources.shared.lock(|shared| {
*shared += 1; *shared += 1;
*shared
});
*cx.resources.e1 += 10; *cx.resources.e1 += 10;
hprintln!("UART0: shared = {}", shared).unwrap(); hprintln!("UART0: shared = {}", shared).unwrap();
hprintln!("UART0:l2 = {}", cx.resources.l2).unwrap(); hprintln!("UART0:l2 = {}", cx.resources.l2).unwrap();
@ -73,9 +75,11 @@ mod app {
// `shared` can be accessed from this context // `shared` can be accessed from this context
// e1 ok (lock_free) // e1 ok (lock_free)
#[task(priority = 1, binds = UART1, resources = [shared, e1])] #[task(priority = 1, binds = UART1, resources = [shared, e1])]
fn uart1(cx: uart1::Context) { fn uart1(mut cx: uart1::Context) {
let shared: &mut u32 = cx.resources.shared; let shared = cx.resources.shared.lock(|shared| {
*shared += 1; *shared += 1;
*shared
});
hprintln!("UART1: shared = {}", shared).unwrap(); hprintln!("UART1: shared = {}", shared).unwrap();
hprintln!("UART1:e1 = {}", cx.resources.e1).unwrap(); hprintln!("UART1:e1 = {}", cx.resources.e1).unwrap();

View file

@ -45,7 +45,7 @@ mod app {
#[task(priority = 2, resources = [shared])] #[task(priority = 2, resources = [shared])]
fn foo(cx: foo::Context) { fn foo(cx: foo::Context) {
let _: cyccnt::Instant = cx.scheduled; let _: cyccnt::Instant = cx.scheduled;
let _: &mut u32 = cx.resources.shared; let _: resources::shared = cx.resources.shared;
let _: foo::Resources = cx.resources; let _: foo::Resources = cx.resources;
} }

View file

@ -70,13 +70,8 @@ pub fn codegen(
// `${task}Resources` // `${task}Resources`
if !task.args.resources.is_empty() { if !task.args.resources.is_empty() {
let (item, constructor) = resources_struct::codegen( let (item, constructor) =
Context::HardwareTask(name), resources_struct::codegen(Context::HardwareTask(name), &mut needs_lt, app);
priority,
&mut needs_lt,
app,
analysis,
);
root.push(item); root.push(item);

View file

@ -37,8 +37,7 @@ pub fn codegen(
let name = &idle.name; let name = &idle.name;
if !idle.args.resources.is_empty() { if !idle.args.resources.is_empty() {
let (item, constructor) = let (item, constructor) = resources_struct::codegen(Context::Idle, &mut needs_lt, app);
resources_struct::codegen(Context::Idle, 0, &mut needs_lt, app, analysis);
root_idle.push(item); root_idle.push(item);
mod_app = Some(constructor); mod_app = Some(constructor);

View file

@ -82,8 +82,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
let mut mod_app = None; let mut mod_app = None;
if !init.args.resources.is_empty() { if !init.args.resources.is_empty() {
let (item, constructor) = let (item, constructor) = resources_struct::codegen(Context::Init, &mut needs_lt, app);
resources_struct::codegen(Context::Init, 0, &mut needs_lt, app, analysis);
root_init.push(item); root_init.push(item);
mod_app = Some(constructor); mod_app = Some(constructor);

View file

@ -49,7 +49,8 @@ pub fn codegen(
)); ));
} }
if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) { let r_prop = &res.properties;
if !r_prop.task_local && !r_prop.lock_free {
mod_resources.push(quote!( mod_resources.push(quote!(
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#(#cfgs)* #(#cfgs)*
@ -83,13 +84,20 @@ pub fn codegen(
) )
}; };
let ceiling = match analysis.ownerships.get(name) {
Some(Ownership::Owned { priority }) => *priority,
Some(Ownership::CoOwned { priority }) => *priority,
Some(Ownership::Contended { ceiling }) => *ceiling,
None => 0,
};
mod_app.push(util::impl_mutex( mod_app.push(util::impl_mutex(
extra, extra,
cfgs, cfgs,
true, true,
name, name,
quote!(#ty), quote!(#ty),
*ceiling, ceiling,
ptr, ptr,
)); ));
} }

View file

@ -2,15 +2,9 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::quote; use quote::quote;
use rtic_syntax::{ast::App, Context}; use rtic_syntax::{ast::App, Context};
use crate::{analyze::Analysis, codegen::util}; use crate::codegen::util;
pub fn codegen( pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) {
ctxt: Context,
priority: u8,
needs_lt: &mut bool,
app: &App,
analysis: &Analysis,
) -> (TokenStream2, TokenStream2) {
let mut lt = None; let mut lt = None;
let resources = match ctxt { let resources = match ctxt {
@ -30,6 +24,7 @@ pub fn codegen(
let cfgs = &res.cfgs; let cfgs = &res.cfgs;
has_cfgs |= !cfgs.is_empty(); has_cfgs |= !cfgs.is_empty();
// access hold if the resource is [x] (exclusive) or [&x] (shared)
let mut_ = if access.is_exclusive() { let mut_ = if access.is_exclusive() {
Some(quote!(mut)) Some(quote!(mut))
} else { } else {
@ -38,99 +33,66 @@ pub fn codegen(
let ty = &res.ty; let ty = &res.ty;
let mangled_name = util::mangle_ident(&name); let mangled_name = util::mangle_ident(&name);
if ctxt.is_init() { // let ownership = &analysis.ownerships[name];
if !analysis.ownerships.contains_key(name) { let r_prop = &res.properties;
// Owned by `init`
fields.push(quote!(
#(#cfgs)*
pub #name: &'static #mut_ #ty
));
values.push(quote!( if !r_prop.task_local && !r_prop.lock_free {
#(#cfgs)* if access.is_shared() {
#name: &#mut_ #mangled_name
));
} else {
// Owned by someone else
lt = Some(quote!('a)); lt = Some(quote!('a));
fields.push(quote!( fields.push(quote!(
#(#cfgs)* #(#cfgs)*
pub #name: &'a mut #ty pub #name: &'a #ty
));
} else {
// Resource proxy
lt = Some(quote!('a));
fields.push(quote!(
#(#cfgs)*
pub #name: resources::#name<'a>
)); ));
values.push(quote!( values.push(quote!(
#(#cfgs)* #(#cfgs)*
#name: &mut #mangled_name #name: resources::#name::new(priority)
)); ));
// continue as the value has been filled,
continue;
} }
} else { } else {
let ownership = &analysis.ownerships[name]; let lt = if ctxt.runs_once() {
quote!('static)
if ownership.needs_lock(priority) {
if mut_.is_none() {
lt = Some(quote!('a));
fields.push(quote!(
#(#cfgs)*
pub #name: &'a #ty
));
} else {
// Resource proxy
lt = Some(quote!('a));
fields.push(quote!(
#(#cfgs)*
pub #name: resources::#name<'a>
));
values.push(quote!(
#(#cfgs)*
#name: resources::#name::new(priority)
));
continue;
}
} else { } else {
let lt = if ctxt.runs_once() { lt = Some(quote!('a));
quote!('static) quote!('a)
} else { };
lt = Some(quote!('a));
quote!('a)
};
if ownership.is_owned() || mut_.is_none() { fields.push(quote!(
fields.push(quote!( #(#cfgs)*
#(#cfgs)* pub #name: &#lt #mut_ #ty
pub #name: &#lt #mut_ #ty ));
)); }
} else {
fields.push(quote!(
#(#cfgs)*
pub #name: &#lt mut #ty
));
}
}
let is_late = expr.is_none(); let is_late = expr.is_none();
if is_late { if is_late {
let expr = if mut_.is_some() { let expr = if access.is_exclusive() {
quote!(&mut *#mangled_name.as_mut_ptr()) quote!(&mut *#mangled_name.as_mut_ptr())
} else {
quote!(&*#mangled_name.as_ptr())
};
values.push(quote!(
#(#cfgs)*
#name: #expr
));
} else { } else {
values.push(quote!( quote!(&*#mangled_name.as_ptr())
#(#cfgs)* };
#name: &#mut_ #mangled_name
)); values.push(quote!(
} #(#cfgs)*
#name: #expr
));
} else {
values.push(quote!(
#(#cfgs)*
#name: &#mut_ #mangled_name
));
} }
} }

View file

@ -82,13 +82,8 @@ pub fn codegen(
// `${task}Resources` // `${task}Resources`
let mut needs_lt = false; let mut needs_lt = false;
if !task.args.resources.is_empty() { if !task.args.resources.is_empty() {
let (item, constructor) = resources_struct::codegen( let (item, constructor) =
Context::SoftwareTask(name), resources_struct::codegen(Context::SoftwareTask(name), &mut needs_lt, app);
task.args.priority,
&mut needs_lt,
app,
analysis,
);
root.push(item); root.push(item);