diff options
author | 2017-04-20 22:09:30 +0200 | |
---|---|---|
committer | 2017-04-20 22:09:30 +0200 | |
commit | 8e0569c396ddfba2c6195f9e72d674956ac0f51f (patch) | |
tree | 794fc04370219e6f203369e4780d7bfca7ffe55d | |
parent | a568afaf2718dee39b039f42114fb933655d206e (diff) | |
download | rtic-next_claim.tar.gz rtic-next_claim.tar.zst rtic-next_claim.zip |
using boolnext_claim
-rw-r--r-- | src/lib.rs | 338 |
1 files changed, 308 insertions, 30 deletions
@@ -59,36 +59,6 @@ impl<T, C> Resource<T, C> { } } -#[derive(Copy, Clone)] -enum State { - Free, - // Locked, - LockedMut, -} - -/// A resource -pub struct Res<T, CEILING> { - _ceiling: PhantomData<CEILING>, - _state: Cell<State>, - //_state: State, - data: UnsafeCell<T>, -} - -impl<T, C> Res<T, C> { - /// 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<T, CEILING> Resource<T, C<CEILING>> { /// Borrows the resource for the duration of another resource's critical /// section @@ -222,6 +192,313 @@ where } // re-implementation of the original claim API +/// A resource +pub struct Res<T, CEILING> { + _ceiling: PhantomData<CEILING>, + _state: Cell<bool>, + data: UnsafeCell<T>, +} + +impl<T, C> Res<T, C> { + /// 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<T, CEILING> Res<T, C<CEILING>> { + /// 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<TASKPRI> + /// 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<<CEILING as Max<CURRCEIL>>::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<R, TASKPRI, CURRCEIL, F>( + &'static self, + _prio: &P<TASKPRI>, + _curr_ceil: &C<CURRCEIL>, + f: F, + ) -> R + where + F: FnOnce(&mut T, &C<<CEILING as Max<CURRCEIL>>::Output>) -> R, + TASKPRI: Unsigned, + CURRCEIL: Unsigned, + CEILING: GreaterThanOrEqual<TASKPRI> + Max<CURRCEIL> + Level + Unsigned, + { + if self._state.get() { + unsafe { + let curr_ceil = <CURRCEIL>::to_u8(); + let new_ceil = <CEILING>::to_u8(); + if new_ceil > curr_ceil { + let old_basepri = basepri::read(); + basepri_max::write(<CEILING>::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<R, TASKPRI, CURRCEIL, F>( + &'static self, + _prio: &P<TASKPRI>, + _curr_ceil: &C<CURRCEIL>, + f: F, + ) -> R + where + F: FnOnce(&T, &C<<CEILING as Max<CURRCEIL>>::Output>) -> R, + TASKPRI: Unsigned, + CURRCEIL: Unsigned, + CEILING: GreaterThanOrEqual<TASKPRI> + Max<CURRCEIL> + Level + Unsigned, + { + if self._state.get() { + unsafe { + let curr_ceil = <CURRCEIL>::to_u8(); + let new_ceil = <CEILING>::to_u8(); + if new_ceil > curr_ceil { + let old_basepri = basepri::read(); + basepri_max::write(<CEILING>::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<R, TASKPRI, CURRCEIL, F>( + &'static self, + _prio: &P<TASKPRI>, + _curr_ceil: &C<CURRCEIL>, + f: F, + ) -> R + where + F: FnOnce(&mut T, &C<<CEILING as Max<CURRCEIL>>::Output>) -> R, + TASKPRI: Unsigned, + CURRCEIL: Unsigned, + CEILING: GreaterThanOrEqual<TASKPRI> + Max<CURRCEIL> + Level + Unsigned, + { + + let curr_ceil = <CURRCEIL>::to_u8(); + let new_ceil = <CEILING>::to_u8(); + if new_ceil > curr_ceil { + let old_basepri = basepri::read(); + basepri_max::write(<CEILING>::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<R, PRIOTASK, CURRCEIL, F>( + // &'static self, + // _prio: &P<PRIOTASK>, + // _curr_ceil: &C<CURRCEIL>, + // f: F, + // ) -> R + // where + // F: FnOnce(&mut T, &C<<CEILING as Max<CURRCEIL>>::Output>) -> R, + // PRIOTASK: Unsigned, + // CURRCEIL: Unsigned, + // CEILING: GreaterThanOrEqual<PRIOTASK> + Max<CURRCEIL> + Level + Unsigned, + // { + // unsafe { + // match self._state.get() { + // State::Free => { + // let c1 = <CURRCEIL>::to_u8(); + // let c2 = <CEILING>::to_u8(); + // if c2 > c1 { + // let old_basepri = basepri::read(); + // basepri_max::write(<CEILING>::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<T, C> Sync for Res<T, C> +where + C: Ceiling, +{ +} + +/* + +// re-implementation of the original claim API impl<T, CEILING> Res<T, C<CEILING>> { /// 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<P, CEILING> where |