diff --git a/src/lib.rs b/src/lib.rs index 17070825e2..e9b1a24cbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -423,71 +423,6 @@ impl Res> { ret } } - - - // / Locks the resource for the duration of the critical section `f` - // / - // / For the duration of the critical section, tasks whose priority level is - // / smaller than or equal to the resource `CEILING` will be prevented from - // / preempting the current task. - // / - // / Within this critical section, resources with ceiling equal to or smaller - // / than `CEILING` can be borrowed at zero cost. See - // / [Resource.borrow](struct.Resource.html#method.borrow). - // #[cfg(not(thumbv6m))] - // pub fn claim_mut( - // &'static self, - // _prio: &P, - // _curr_ceil: &C, - // f: F, - // ) -> R - // where - // F: FnOnce(&mut T, &C<>::Output>) -> R, - // PRIOTASK: Unsigned, - // CURRCEIL: Unsigned, - // CEILING: GreaterThanOrEqual + Max + Level + Unsigned, - // { - // unsafe { - // match self._state.get() { - // State::Free => { - // let c1 = ::to_u8(); - // let c2 = ::to_u8(); - // if c2 > c1 { - // let old_basepri = basepri::read(); - // basepri_max::write(::hw()); - // barrier!(); - // self._state.set(State::LockedMut); - // barrier!(); - // - // let ret = f( - // &mut *self.data.get(), - // &C { _marker: PhantomData }, - // ); - // - // barrier!(); - // self._state.set(State::Free); - // barrier!(); - // basepri::write(old_basepri); - // ret - // } else { - // self._state.set(State::LockedMut); - // barrier!(); - // - // let ret = f( - // &mut *self.data.get(), - // &C { _marker: PhantomData }, - // ); - // - // barrier!(); - // self._state.set(State::Free); - // ret - // - // } - // } - // _ => panic!("Resource already locked)"), - // } - // } - // } } unsafe impl Sync for Res @@ -496,145 +431,304 @@ where { } -/* -// re-implementation of the original claim API -impl Res> { - /// Locks the resource for the duration of the critical section `f` - /// - /// For the duration of the critical section, tasks whose priority level is - /// smaller than or equal to the resource `CEILING` will be prevented from - /// preempting the current task. - /// - /// Within this critical section, resources with ceiling equal to or smaller - /// than `CEILING` can be borrowed at zero cost. See - /// [Resource.borrow](struct.Resource.html#method.borrow). - #[cfg(not(thumbv6m))] - pub fn claim( - &'static self, - _prio: &P, - _curr_ceil: &C, - f: F, - ) -> R +/// Nem attempt +use core::cell::RefCell; +use core::cell::RefMut; + +//use core::borrow::BorrowMut; +/// A resource +pub struct ResRef { + _ceiling: PhantomData, + data: UnsafeCell>, +} + +impl ResRef { + /// Creates a new resource with ceiling `C` + pub const fn new(data: T) -> Self where - F: FnOnce(Ref, &C<>::Output>) -> R, - PRIOTASK: Unsigned, - CURRCEIL: Unsigned, - CEILING: GreaterThanOrEqual + Max + Level + Unsigned, + C: Ceiling, { - - match self._state.get() { - State::LockedMut => panic!("Resource already locked)"), - _ => unsafe { - let c1 = ::to_u8(); - let c2 = ::to_u8(); - if c2 > c1 { - let old_basepri = basepri::read(); - basepri_max::write(::hw()); - barrier!(); - let s = self._state.get(); - self._state.set(State::LockedMut); - barrier!(); - - let ret = f( - Ref::new(&*self.data.get()), - &C { _marker: PhantomData }, - ); - - barrier!(); - self._state.set(s); - barrier!(); - basepri::write(old_basepri); - ret - } else { - let s = self._state.get(); - self._state.set(State::LockedMut); - barrier!(); - - let ret = f( - Ref::new(&*self.data.get()), - &C { _marker: PhantomData }, - ); - - barrier!(); - self._state.set(s); - ret - } - }, + ResRef { + _ceiling: PhantomData, + data: UnsafeCell::new(RefCell::new(data)), } } +} + +impl ResRef> { /// Locks the resource for the duration of the critical section `f` /// /// For the duration of the critical section, tasks whose priority level is /// smaller than or equal to the resource `CEILING` will be prevented from /// preempting the current task. /// - /// Within this critical section, resources with ceiling equal to or smaller - /// than `CEILING` can be borrowed at zero cost. See - /// [Resource.borrow](struct.Resource.html#method.borrow). + /// claim takes three args of type R, PRIOTASK, CURRCEIL, F + /// R is the Resource to lock (self) + /// PRIOTASK is the priority of the task (context) calling claim + /// CURRCEIL is the current system ceiling + /// + /// F is the type of the closure, hande &T and &C + /// &T is a read only reference to the data + /// &C is the new system ceiling + /// + /// Usage example: a task at prio P1, claiming R1 and R2. + /// fn j(_task: Exti0, p: P1) { + /// R1.claim_mut( + /// &p, &p.as_ceiling(), |a, c| { + /// R2.claim_mut( + /// &p, &c, |b, _| { + /// b.y = a[0]; // b is mutable + /// a[1] = b.y; // a is mutable + /// } + /// ); + /// a[2] = 0; // a is mutable + /// } + /// ); + /// } + /// The implementation satisfies the following + /// 1. Race free access to the resources under the Stack Resource Policy (SRP) + /// System ceiling SC, is implemented by the NVIC as MAX(TASKPRI, BASEPRPI) + /// Hence in case TASKPRI = SC, the BASEPRI register does not need to be updated + /// as checked by c2 > c1 (this an optimization) + /// + /// 2. Static (compile time verification) that SC >= TASKPRI + /// This is achieved by the type bound CEILING: GreaterThanOrEqual + /// This gives ensurence that no task access a resoure with lower ceiling value than the task + /// and hence satisfies the soundness rule of SRP + /// + /// 3. The system ceileng for the closure CS = MAX(CURRCEIL, R) + /// This is achieved by &C<>::Output> + /// where Max operates on R and CEILING + /// + /// 4. Rust aliasing rules are ensured as run-time check raises a panic if resourse state is locked + /// This resembles the RefCell implementation, but the implementation is more strict as multiple + /// readers are disallowed. Essentially this forbids re-locking + /// + /// Usage example failing: a task at prio P1, claiming R1 and R1. + /// fn j(_task: Exti0, p: P1) { + /// R1.claim_mut( + /// &p, &p.as_ceiling(), |a1, c| { + /// R1.claim_mut( <-- at this point a panic will occur + /// &p, &c, |a2, _| { + /// } + /// ); + /// a1[2] = 0; // a is mutable + /// } + /// ); + /// } + /// + /// 5. The ceiling of the closure cannot be leaked + /// + /// fn jres_opt_leak(_task: Exti0, p: P1) { + /// R1.claim_mut( + /// &p, &p.as_ceiling(), |a, c| { + /// let leak_c = R2.claim_mut(&p, &c, |b, c| c); <-- trying to leak c as a return value + /// + /// R5.claim_mut(&p, leak_c, |b, c| {}); <-- trying to use a leaked system ceilng + /// + /// a[2] = 0; + /// } + /// ); + /// } + /// + /// The compiler will reject leakage due to the lifetime, (c in a closure is a &C, so it cannot be returned) + /// A leakage would indeed be fatal as claim would hand out an unprotected R #[cfg(not(thumbv6m))] - pub fn claim_mut( + pub fn claim_mut( &'static self, - _prio: &P, + _prio: &P, _curr_ceil: &C, f: F, ) -> R where F: FnOnce(&mut T, &C<>::Output>) -> R, - PRIOTASK: Unsigned, + TASKPRI: Unsigned, CURRCEIL: Unsigned, - CEILING: GreaterThanOrEqual + Max + Level + Unsigned, + CEILING: GreaterThanOrEqual + Max + Level + Unsigned, { + unsafe { - match self._state.get() { - State::Free => { - let c1 = ::to_u8(); - let c2 = ::to_u8(); - if c2 > c1 { - let old_basepri = basepri::read(); - basepri_max::write(::hw()); - barrier!(); - self._state.set(State::LockedMut); - barrier!(); + let curr_ceil = ::to_u8(); + let new_ceil = ::to_u8(); + if new_ceil > curr_ceil { + let old_basepri = basepri::read(); + basepri_max::write(::hw()); + barrier!(); - let ret = f( - &mut *self.data.get(), - &C { _marker: PhantomData }, - ); + let r: &RefCell = &*self.data.get(); + let rr: RefCell = *r; + let mut rm: RefMut = rr.borrow_mut(); + let mut t: T = *rm; + let ret = f(&mut t, &C { _marker: PhantomData }); - barrier!(); - self._state.set(State::Free); - barrier!(); - basepri::write(old_basepri); - ret - } else { - self._state.set(State::LockedMut); - barrier!(); - - let ret = f( - &mut *self.data.get(), - &C { _marker: PhantomData }, - ); - - barrier!(); - self._state.set(State::Free); - ret - - } - } - _ => panic!("Resource already locked)"), + barrier!(); + basepri::write(old_basepri); + ret + } else { + panic!(""); } } } } -unsafe impl Sync for Res +// +// /// Read only claim, see claim_mut above +// #[cfg(not(thumbv6m))] +// pub fn claim( +// &'static self, +// _prio: &P, +// _curr_ceil: &C, +// f: F, +// ) -> R +// where +// F: FnOnce(&T, &C<>::Output>) -> R, +// TASKPRI: Unsigned, +// CURRCEIL: Unsigned, +// CEILING: GreaterThanOrEqual + Max + Level + Unsigned, +// { +// if self._state.get() { +// unsafe { +// let curr_ceil = ::to_u8(); +// let new_ceil = ::to_u8(); +// if new_ceil > curr_ceil { +// let old_basepri = basepri::read(); +// basepri_max::write(::hw()); +// barrier!(); +// self._state.set(false); +// barrier!(); +// +// let ret = f(&*self.data.get(), &C { _marker: PhantomData }); +// +// barrier!(); +// self._state.set(true); +// barrier!(); +// basepri::write(old_basepri); +// ret +// } else { +// self._state.set(false); +// barrier!(); +// +// let ret = f(&*self.data.get(), &C { _marker: PhantomData }); +// +// barrier!(); +// self._state.set(true); +// ret +// } +// } +// } else { +// panic!("Resource already locked)") +// } +// } +// +// /// Unsafe version of claim_mut +// #[cfg(not(thumbv6m))] +// pub unsafe fn claim_mut_unsafe( +// &'static self, +// _prio: &P, +// _curr_ceil: &C, +// f: F, +// ) -> R +// where +// F: FnOnce(&mut T, &C<>::Output>) -> R, +// TASKPRI: Unsigned, +// CURRCEIL: Unsigned, +// CEILING: GreaterThanOrEqual + Max + Level + Unsigned, +// { +// +// let curr_ceil = ::to_u8(); +// let new_ceil = ::to_u8(); +// if new_ceil > curr_ceil { +// let old_basepri = basepri::read(); +// basepri_max::write(::hw()); +// barrier!(); +// +// let ret = f(&mut *self.data.get(), &C { _marker: PhantomData }); +// +// barrier!(); +// basepri::write(old_basepri); +// ret +// } else { +// +// let ret = f(&mut *self.data.get(), &C { _marker: PhantomData }); +// +// ret +// } +// } +// +// +// // / Locks the resource for the duration of the critical section `f` +// // / +// // / For the duration of the critical section, tasks whose priority level is +// // / smaller than or equal to the resource `CEILING` will be prevented from +// // / preempting the current task. +// // / +// // / Within this critical section, resources with ceiling equal to or smaller +// // / than `CEILING` can be borrowed at zero cost. See +// // / [Resource.borrow](struct.Resource.html#method.borrow). +// // #[cfg(not(thumbv6m))] +// // pub fn claim_mut( +// // &'static self, +// // _prio: &P, +// // _curr_ceil: &C, +// // f: F, +// // ) -> R +// // where +// // F: FnOnce(&mut T, &C<>::Output>) -> R, +// // PRIOTASK: Unsigned, +// // CURRCEIL: Unsigned, +// // CEILING: GreaterThanOrEqual + Max + Level + Unsigned, +// // { +// // unsafe { +// // match self._state.get() { +// // State::Free => { +// // let c1 = ::to_u8(); +// // let c2 = ::to_u8(); +// // if c2 > c1 { +// // let old_basepri = basepri::read(); +// // basepri_max::write(::hw()); +// // barrier!(); +// // self._state.set(State::LockedMut); +// // barrier!(); +// // +// // let ret = f( +// // &mut *self.data.get(), +// // &C { _marker: PhantomData }, +// // ); +// // +// // barrier!(); +// // self._state.set(State::Free); +// // barrier!(); +// // basepri::write(old_basepri); +// // ret +// // } else { +// // self._state.set(State::LockedMut); +// // barrier!(); +// // +// // let ret = f( +// // &mut *self.data.get(), +// // &C { _marker: PhantomData }, +// // ); +// // +// // barrier!(); +// // self._state.set(State::Free); +// // ret +// // +// // } +// // } +// // _ => panic!("Resource already locked)"), +// // } +// // } +// // } +//} +// +unsafe impl Sync for ResRef where C: Ceiling, { } -*/ + /// A hardware peripheral as a resource pub struct Peripheral where