diff --git a/examples/custom-type.rs b/examples/custom-type.rs new file mode 100644 index 0000000000..79d6cc461c --- /dev/null +++ b/examples/custom-type.rs @@ -0,0 +1,50 @@ +#![deny(unsafe_code)] +#![deny(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m_rtfm as rtfm; +extern crate stm32f103xx; + +use rtfm::{app, Threshold}; + +pub struct Foo; + +app! { + device: stm32f103xx, + + resources: { + static CO_OWNED: Foo = Foo; + static ON: Foo = Foo; + static OWNED: Foo = Foo; + static SHARED: Foo = Foo; + }, + + idle: { + resources: [OWNED, SHARED], + }, + + tasks: { + SYS_TICK: { + path: sys_tick, + resources: [CO_OWNED, ON, SHARED], + }, + + TIM2: { + enabled: false, + path: tim2, + priority: 1, + resources: [CO_OWNED], + }, + }, +} + +fn init(_p: ::init::Peripherals, _r: ::init::Resources) {} + +fn idle(_t: &mut Threshold, _r: ::idle::Resources) -> ! { + loop {} +} + +fn sys_tick(_t: &mut Threshold, _r: SYS_TICK::Resources) {} + +fn tim2(_t: &mut Threshold, _r: TIM2::Resources) {} diff --git a/examples/full-syntax.rs b/examples/full-syntax.rs index b84077fb34..5b27412265 100644 --- a/examples/full-syntax.rs +++ b/examples/full-syntax.rs @@ -73,12 +73,12 @@ mod main { } } -fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { +fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { *r.ON = !*r.ON; *r.CO_OWNED += 1; } -fn tim2(_t: &mut Threshold, r: TIM2::Resources) { +fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) { *r.CO_OWNED += 1; } diff --git a/examples/generics.rs b/examples/generics.rs index 7cf9257b32..ca7726d0b7 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -70,5 +70,5 @@ fn exti0(t: &mut Threshold, r: EXTI0::Resources) { // This task has direct access to the resources fn exti1(t: &mut Threshold, r: EXTI1::Resources) { - work(t, r.GPIOA, r.SPI1); + work(t, &r.GPIOA, &r.SPI1); } diff --git a/examples/late-resources.rs b/examples/late-resources.rs index d42431c28a..07c321f673 100644 --- a/examples/late-resources.rs +++ b/examples/late-resources.rs @@ -1,5 +1,4 @@ //! Demonstrates initialization of resources in `init`. - #![deny(unsafe_code)] #![deny(warnings)] #![feature(proc_macro)] diff --git a/examples/one-task.rs b/examples/one-task.rs index 90eb459a2c..07def59ba7 100644 --- a/examples/one-task.rs +++ b/examples/one-task.rs @@ -77,7 +77,7 @@ fn idle() -> ! { // `r` is the set of resources this task has access to. `SYS_TICK::Resources` // has one field per resource declared in `app!`. #[allow(unsafe_code)] -fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { +fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { // toggle state *r.ON = !*r.ON; diff --git a/examples/preemption.rs b/examples/preemption.rs index 07e9362150..8e501887c3 100644 --- a/examples/preemption.rs +++ b/examples/preemption.rs @@ -42,7 +42,7 @@ fn idle() -> ! { } } -fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { +fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { // .. // This task can't be preempted by `tim2` so it has direct access to the diff --git a/examples/two-tasks.rs b/examples/two-tasks.rs index e9d31e783d..4f567f0cea 100644 --- a/examples/two-tasks.rs +++ b/examples/two-tasks.rs @@ -42,7 +42,7 @@ fn idle() -> ! { // As both tasks are running at the same priority one can't preempt the other. // Thus both tasks have direct access to the resource -fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { +fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) { // .. *r.COUNTER += 1; @@ -50,7 +50,7 @@ fn sys_tick(_t: &mut Threshold, r: SYS_TICK::Resources) { // .. } -fn tim2(_t: &mut Threshold, r: TIM2::Resources) { +fn tim2(_t: &mut Threshold, mut r: TIM2::Resources) { // .. *r.COUNTER += 1; diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 0fc125daba..65d98e6960 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -17,6 +17,13 @@ pub enum Ownership { } impl Ownership { + pub fn ceiling(&self) -> u8 { + match *self { + Ownership::Owned { priority } => priority, + Ownership::Shared { ceiling } => ceiling, + } + } + pub fn is_owned(&self) -> bool { match *self { Ownership::Owned { .. } => true, diff --git a/macros/src/trans.rs b/macros/src/trans.rs index b209b8ef53..1008dfedab 100644 --- a/macros/src/trans.rs +++ b/macros/src/trans.rs @@ -1,7 +1,7 @@ use quote::{Ident, Tokens}; use syn::{Lit, StrStyle}; -use analyze::{Ownership, Ownerships}; +use analyze::Ownerships; use check::{App, Kind}; fn krate() -> Ident { @@ -81,11 +81,11 @@ fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec, root: &mut V }); } else { rfields.push(quote! { - pub #name: #super_::_resource::#name, + pub #name: ::idle::#name, }); rexprs.push(quote! { - #name: #super_::_resource::#name::new(), + #name: ::idle::#name { _0: core::marker::PhantomData }, }); } } @@ -126,6 +126,85 @@ fn idle(app: &App, ownerships: &Ownerships, main: &mut Vec, root: &mut V exprs.push(quote!(unsafe { idle::Resources::new() })); } + let device = &app.device; + for name in &app.idle.resources { + let ceiling = ownerships[name].ceiling(); + + // owned resource + if ceiling == 0 { + continue + } + + let _name = Ident::new(format!("_{}", name.as_ref())); + let resource = app.resources + .get(name) + .expect(&format!("BUG: resource {} has no definition", name)); + + let ty = &resource.ty; + let _static = if resource.expr.is_some() { + quote!(#_name) + } else { + quote!(#_name.some) + }; + + mod_items.push(quote! { + #[allow(non_camel_case_types)] + pub struct #name { _0: core::marker::PhantomData<*const ()> } + }); + + root.push(quote! { + #[allow(unsafe_code)] + unsafe impl #krate::Resource for idle::#name { + type Data = #ty; + + fn borrow<'cs>(&'cs self, t: &'cs Threshold) -> &'cs Self::Data { + assert!(t.value() >= #ceiling); + + unsafe { &#_static } + } + + fn borrow_mut<'cs>( + &'cs mut self, + t: &'cs Threshold, + ) -> &'cs mut Self::Data { + assert!(t.value() >= #ceiling); + + unsafe { &mut #_static } + } + + fn claim(&self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&Self::Data, &mut Threshold) -> R + { + unsafe { + #krate::claim( + &#_static, + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + + fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&mut Self::Data, &mut Threshold) -> R + { + unsafe { + #krate::claim( + &mut #_static, + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + } + }); + } + if !mod_items.is_empty() { root.push(quote! { #[allow(unsafe_code)] @@ -323,234 +402,153 @@ fn init(app: &App, main: &mut Vec, root: &mut Vec) { fn resources(app: &App, ownerships: &Ownerships, root: &mut Vec) { let krate = krate(); - let device = &app.device; - let mut items = vec![]; - let mut impls = vec![]; - for (name, ownership) in ownerships { + for name in ownerships.keys() { let _name = Ident::new(format!("_{}", name.as_ref())); - if let Some(resource) = app.resources.get(name) { - // Declare the static that holds the resource - let expr = &resource.expr; - let ty = &resource.ty; + // Declare the static that holds the resource + let resource = app.resources + .get(name) + .expect(&format!("BUG: resource {} has no definition", name)); - root.push(match *expr { - Some(ref expr) => quote! { - static mut #_name: #ty = #expr; - }, - None => quote! { - // Resource initialized in `init` - static mut #_name: #krate::UntaggedOption<#ty> = #krate::UntaggedOption { none: () }; - }, - }); - } + let expr = &resource.expr; + let ty = &resource.ty; - let mut impl_items = vec![]; - - match *ownership { - Ownership::Owned { .. } => { - // For owned resources we don't need claim() or borrow() - } - Ownership::Shared { ceiling } => { - let resource = app.resources - .get(name) - .expect(&format!("BUG: resource {} has no definition", name)); - let ty = &resource.ty; - let res_rvalue = if resource.expr.is_some() { - quote!(#_name) - } else { - quote!(#_name.some) - }; - - impl_items.push(quote! { - type Data = #ty; - - fn borrow<'cs>( - &'cs self, - t: &'cs #krate::Threshold, - ) -> &'cs #ty { - assert!(t.value() >= #ceiling); - - unsafe { &#res_rvalue } - } - - fn borrow_mut<'cs>( - &'cs mut self, - t: &'cs #krate::Threshold, - ) -> &'cs mut #ty { - assert!(t.value() >= #ceiling); - - unsafe { - &mut #res_rvalue - } - } - - fn claim( - &self, - t: &mut #krate::Threshold, - f: F, - ) -> R - where - F: FnOnce( - &#ty, - &mut #krate::Threshold) -> R - { - unsafe { - #krate::claim( - &#res_rvalue, - #ceiling, - #device::NVIC_PRIO_BITS, - t, - f, - ) - } - } - - fn claim_mut( - &mut self, - t: &mut #krate::Threshold, - f: F, - ) -> R - where - F: FnOnce( - &mut #ty, - &mut #krate::Threshold) -> R - { - unsafe { - #krate::claim( - &mut #res_rvalue, - #ceiling, - #device::NVIC_PRIO_BITS, - t, - f, - ) - } - } - }); - - impls.push(quote! { - #[allow(unsafe_code)] - unsafe impl #krate::Resource for _resource::#name { - #(#impl_items)* - } - }); - - items.push(quote! { - #[allow(non_camel_case_types)] - pub struct #name { _0: PhantomData<*const ()> } - - #[allow(unsafe_code)] - impl #name { - pub unsafe fn new() -> Self { - #name { _0: PhantomData } - } - } - }); - } - } + root.push(match *expr { + Some(ref expr) => quote! { + static mut #_name: #ty = #expr; + }, + None => quote! { + // Resource initialized in `init` + static mut #_name: #krate::UntaggedOption<#ty> = + #krate::UntaggedOption { none: () }; + }, + }); } - - if !items.is_empty() { - root.push(quote! { - #[allow(unsafe_code)] - mod _resource { - use core::marker::PhantomData; - - #(#items)* - } - }) - } - root.push(quote! { - #(#impls)* - }); } fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { let device = &app.device; let krate = krate(); - for (name, task) in &app.tasks { + for (tname, task) in &app.tasks { let mut exprs = vec![]; let mut fields = vec![]; let mut items = vec![]; - let mut lifetime = None; - let mut needs_reexport = false; - let mut needs_threshold = false; let has_resources = !task.resources.is_empty(); if has_resources { - needs_threshold = !task.resources.is_empty(); + for rname in &task.resources { + let ceiling = ownerships[rname].ceiling(); + let _rname = Ident::new(format!("_{}", rname.as_ref())); + let resource = app.resources + .get(rname) + .expect(&format!("BUG: resource {} has no definition", rname)); - for name in &task.resources { - let _name = Ident::new(format!("_{}", name.as_ref())); + let ty = &resource.ty; + let _static = if resource.expr.is_some() { + quote!(#_rname) + } else { + quote!(#_rname.some) + }; - match ownerships[name] { - Ownership::Shared { ceiling } if ceiling > task.priority => { - needs_threshold = true; - - fields.push(quote! { - pub #name: ::_resource::#name, - }); - - exprs.push(quote! { - #name: { - ::_resource::#name::new() - }, - }); - } - _ => { - lifetime = Some(quote!('a)); - let resource = app.resources - .get(name) - .expect(&format!("BUG: resource {} has no definition", name)); - - needs_reexport = true; - let ty = &resource.ty; - - fields.push(quote! { - pub #name: &'a mut #ty, - }); - - exprs.push(if resource.expr.is_some() { - quote! { - #name: &mut ::#_name, - } - } else { - quote! { - #name: ::#_name.as_mut(), - } - }); - } - } - } - - if needs_reexport { - let rname = Ident::new(format!("_{}Resources", name)); - root.push(quote! { + items.push(quote! { #[allow(non_camel_case_types)] - #[allow(non_snake_case)] - pub struct #rname<#lifetime> { - #(#fields)* + pub struct #rname { _0: PhantomData<*const ()> } + }); + + root.push(quote! { + #[allow(unsafe_code)] + unsafe impl #krate::Resource for #tname::#rname { + type Data = #ty; + + fn borrow<'cs>(&'cs self, t: &'cs Threshold) -> &'cs Self::Data { + assert!(t.value() >= #ceiling); + + unsafe { &#_static } + } + + fn borrow_mut<'cs>( + &'cs mut self, + t: &'cs Threshold, + ) -> &'cs mut Self::Data { + assert!(t.value() >= #ceiling); + + unsafe { &mut #_static } + } + + fn claim(&self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&Self::Data, &mut Threshold) -> R + { + unsafe { + #krate::claim( + &#_static, + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } + + fn claim_mut(&mut self, t: &mut Threshold, f: F) -> R + where + F: FnOnce(&mut Self::Data, &mut Threshold) -> R + { + unsafe { + #krate::claim( + &mut #_static, + #ceiling, + #device::NVIC_PRIO_BITS, + t, + f, + ) + } + } } }); - items.push(quote! { - pub use ::#rname as Resources; + if ceiling <= task.priority { + root.push(quote! { + #[allow(unsafe_code)] + impl core::ops::Deref for #tname::#rname { + type Target = #ty; + + fn deref(&self) -> &Self::Target { + unsafe { &#_static } + } + } + + #[allow(unsafe_code)] + impl core::ops::DerefMut for #tname::#rname { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut #_static } + } + } + }) + } + + fields.push(quote! { + pub #rname: #rname, }); - } else { - items.push(quote! { - #[allow(non_snake_case)] - pub struct Resources<#lifetime> { - #(#fields)* - } + + exprs.push(quote! { + #rname: #rname { _0: PhantomData }, }); } + items.push(quote! { + #[allow(non_snake_case)] + pub struct Resources { + #(#fields)* + } + }); + items.push(quote! { #[allow(unsafe_code)] - impl<#lifetime> Resources<#lifetime> { + impl Resources { pub unsafe fn new() -> Self { Resources { #(#exprs)* @@ -564,7 +562,7 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { let mut exprs = vec![]; let priority = task.priority; - if needs_threshold { + if has_resources { tys.push(quote!(&mut #krate::Threshold)); exprs.push(quote! { &mut if #priority == 1 << #device::NVIC_PRIO_BITS { @@ -576,18 +574,18 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { } if has_resources { - tys.push(quote!(#name::Resources)); - exprs.push(quote!(#name::Resources::new())); + tys.push(quote!(#tname::Resources)); + exprs.push(quote!(#tname::Resources::new())); } let path = &task.path; - let _name = Ident::new(format!("_{}", name)); - let export_name = Lit::Str(name.as_ref().to_owned(), StrStyle::Cooked); + let _tname = Ident::new(format!("_{}", tname)); + let export_name = Lit::Str(tname.as_ref().to_owned(), StrStyle::Cooked); root.push(quote! { #[allow(non_snake_case)] #[allow(unsafe_code)] #[export_name = #export_name] - pub unsafe extern "C" fn #_name() { + pub unsafe extern "C" fn #_tname() { let f: fn(#(#tys,)*) = #path; f(#(#exprs,)*) @@ -597,7 +595,9 @@ fn tasks(app: &App, ownerships: &Ownerships, root: &mut Vec) { root.push(quote!{ #[allow(non_snake_case)] #[allow(unsafe_code)] - mod #name { + mod #tname { + use core::marker::PhantomData; + #[allow(dead_code)] #[deny(const_err)] const CHECK_PRIORITY: (u8, u8) = ( diff --git a/tests/cfail/lock.rs b/tests/cfail/lock.rs index 5630649aeb..eb03b7d541 100644 --- a/tests/cfail/lock.rs +++ b/tests/cfail/lock.rs @@ -45,22 +45,24 @@ fn idle() -> ! { } fn exti0(mut t: &mut Threshold, mut r: EXTI0::Resources) { + // ERROR need to lock to access the resource because priority < ceiling + if *r.ON { + //~^ error type `EXTI0::ON` cannot be dereferenced + } + // OK need to lock to access the resource - if r.ON.claim(&mut t, |on, _| **on) {} + if r.ON.claim(&mut t, |on, _| *on) {} // OK can claim a resource with maximum ceiling - r.MAX.claim_mut(&mut t, |max, _| **max += 1); + r.MAX.claim_mut(&mut t, |max, _| *max += 1); } fn exti1(mut t: &mut Threshold, r: EXTI1::Resources) { - // ERROR no need to lock. Has direct access because priority == ceiling - if (**r.ON).claim(&mut t, |on, _| **on) { - //~^ error no method named `claim` found for type - } + // OK to directly access the resource because priority == ceiling + if *r.ON {} - if **r.ON { - // OK - } + // though the resource can still be claimed -- the claim is a no-op + if r.ON.claim(&mut t, |on, _| *on) {} } fn exti2(_t: &mut Threshold, _r: EXTI2::Resources) {} diff --git a/tests/cfail/token-transfer.rs b/tests/cfail/token-transfer.rs index bc6205217a..f92e4b2b73 100644 --- a/tests/cfail/token-transfer.rs +++ b/tests/cfail/token-transfer.rs @@ -9,7 +9,7 @@ extern crate stm32f103xx; use rtfm::{app, Threshold}; -app! { //~ error bound `rtfm::Threshold: core::marker::Send` is not satisfied +app! { //~ error bound `*const (): core::marker::Send` is not satisfied device: stm32f103xx, resources: {