diff --git a/src/lib.rs b/src/lib.rs index 91f3a54cd1..17070825e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,36 +59,6 @@ impl Resource { } } -#[derive(Copy, Clone)] -enum State { - Free, - // Locked, - LockedMut, -} - -/// A resource -pub struct Res { - _ceiling: PhantomData, - _state: Cell, - //_state: State, - data: UnsafeCell, -} - -impl Res { - /// Creates a new resource with ceiling `C` - pub const fn new(data: T) -> Self - where - C: Ceiling, - { - Res { - _ceiling: PhantomData, - _state: Cell::new(State::Free), - // _state: State::Free, - data: UnsafeCell::new(data), - } - } -} - impl Resource> { /// Borrows the resource for the duration of another resource's critical /// section @@ -221,6 +191,313 @@ where { } +// re-implementation of the original claim API +/// A resource +pub struct Res { + _ceiling: PhantomData, + _state: Cell, + data: UnsafeCell, +} + +impl Res { + /// Creates a new resource with ceiling `C` + pub const fn new(data: T) -> Self + where + C: Ceiling, + { + Res { + _ceiling: PhantomData, + _state: Cell::new(true), + // _state: State::Free, + data: UnsafeCell::new(data), + } + } +} + +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. + /// + /// 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( + &'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, + { + 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(&mut *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(&mut *self.data.get(), &C { _marker: PhantomData }); + + barrier!(); + self._state.set(true); + ret + } + } + } else { + panic!("Resource already locked)") + } + } + + /// 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 Res +where + C: Ceiling, +{ +} + +/* + // re-implementation of the original claim API impl Res> { /// Locks the resource for the duration of the critical section `f` @@ -357,6 +634,7 @@ where { } +*/ /// A hardware peripheral as a resource pub struct Peripheral where