aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar pln <Per Lindgren> 2017-04-20 22:09:30 +0200
committerGravatar pln <Per Lindgren> 2017-04-20 22:09:30 +0200
commit8e0569c396ddfba2c6195f9e72d674956ac0f51f (patch)
tree794fc04370219e6f203369e4780d7bfca7ffe55d
parenta568afaf2718dee39b039f42114fb933655d206e (diff)
downloadrtic-next_claim.tar.gz
rtic-next_claim.tar.zst
rtic-next_claim.zip
using boolnext_claim
-rw-r--r--src/lib.rs338
1 files changed, 308 insertions, 30 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 91f3a54c..17070825 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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