diff options
author | Brian Cully <bjc@kublai.com> | 2019-05-10 11:50:53 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-06-03 09:23:29 -0400 |
commit | 5781e9391fe3d7d3dabec620cb782d38f5f9cb9e (patch) | |
tree | 9ed0b09fd40f128733e8441fc63f91a9ee17d3f6 /src/array.rs | |
parent | f12811a0a5e15b596a0cc06c095832a6b795172b (diff) | |
download | clint-5781e9391fe3d7d3dabec620cb782d38f5f9cb9e.tar.gz clint-5781e9391fe3d7d3dabec620cb782d38f5f9cb9e.zip |
Update to 0.2.0: Add HandlerArray type.v0.2.0
* Create HandlerArray as a safe wrapper around Handler.
* Add Cargo features for HandlerArray size.
* Move Handler into sub-module.
* Add CriticalSection sub-module for architecture dependent support
of interrupt-free contexts.
* Add build rules to pull in cortex-m support for CriticalSection
automatically.
Diffstat (limited to 'src/array.rs')
-rw-r--r-- | src/array.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/array.rs b/src/array.rs new file mode 100644 index 0000000..e01fe74 --- /dev/null +++ b/src/array.rs @@ -0,0 +1,171 @@ +//! Safe wrapper for closure-based interrupt handlers. +//! +//! # Notes +//! +//! The number of entries allowed is defined by Cargo features. The +//! default is 32 as this seems a reasonable comprimise between the +//! size of the array and utility. Each array entry costs two words of +//! space for the closure reference. Thus a full array of 256 entries +//! on a 32-bit architecture costs 2048 bytes of memory, which can be +//! quite a lot on resource constrained devices. +//! +//! One day, when const-generics are stabilized, this will be more +//! elegant. +//! +//! # Examples +//! +//! ``` no_run +//! use clint::HandlerArray; +//! use cortex_m_rt::exception; +//! +//! static HANDLERS: HandlerArray = HandlerArray::new(); +//! +//! fn main() { +//! // NB: This closure has to be created outside of `with_overrides` to +//! // ensure it lives as long as `with_overrides` scope lasts. +//! let mut cl = || { +//! // Your interrupt handling code. +//! }; +//! HANDLERS.with_overrides(|arr| { +//! arr.register(0, &mut cl); +//! +//! loop { +//! // Your main loop. +//! } +//! }) +//! } +//! +//! #[exception] +//! fn SysTick() { +//! HANDLERS.call(0); +//! } +//! ``` + +use crate::cs::{CriticalSection, Locker}; +use crate::Handler; + +use core::cell::UnsafeCell; + +// Define features for the underlying array size so that we can +// statically allocate it. +// TODO: Use const generics when available. +#[cfg(feature = "isr-8")] +const NR_ISR: usize = 8; +#[cfg(feature = "isr-16")] +const NR_ISR: usize = 16; +#[cfg(feature = "isr-32")] +const NR_ISR: usize = 32; +#[cfg(feature = "isr-64")] +const NR_ISR: usize = 64; +#[cfg(feature = "isr-128")] +const NR_ISR: usize = 128; +#[cfg(feature = "isr-256")] +const NR_ISR: usize = 256; + +/// Safely use `Handler`s by enclosing them in an array. +/// +/// This type provides a safe wrapper around `Handler` by ensuring +/// that closures are swapped safely using critical sections, and that +/// the lifetime of those handlers is sufficient by using the inner +/// scope of `with_overrides`/`lock_overrides`. +#[derive(Debug)] +pub struct HandlerArray<'a> { + h: UnsafeCell<[Handler<'a>; NR_ISR]>, +} + +impl<'a> HandlerArray<'a> { + /// Create a new `HandlerArray` filled with no-op handlers. + pub const fn new() -> Self { + Self { + h: UnsafeCell::new([Handler::new(); NR_ISR]), + } + } + + /// Register `f` for entry `nr` in this array using the default + /// critical section locker. + pub fn register<F>(&self, nr: usize, f: &'a mut F) + where + F: FnMut() + Send + 'a, + { + self.lock_register(&Locker::new(), nr, f) + } + + /// Register `f` for entry `nr` in this array using `cs` to create + /// a critical section for updating the array. + pub fn lock_register<F, CS>(&self, cs: &CS, nr: usize, f: &'a mut F) + where + F: FnMut() + Send + 'a, + CS: CriticalSection, + { + cs.with_lock(|| unsafe { (*self.h.get())[nr].replace(f) }); + } + + /// Call the handler for entry `nr`. + pub fn call(&self, nr: usize) { + // Unsafe: there's always a valid handler to call except for + // when it's being actively replaced. As long as that happens + // while in a critical section, there's no risk of data races. + unsafe { (*self.h.get())[nr].call() } + } + + /// Create a new array for use in `f`'s scope. The existing + /// handlers can be overridden using `register` or + /// `lock_register`. When `f` exits, all previous handlers are + /// restored. + pub fn with_overrides<'b>(&self, f: impl FnOnce(&HandlerArray<'b>)) { + self.lock_overrides(&Locker::new(), f) + } + + /// Same as `with_overrides` but allows you to specify your own + /// implementation of `CriticalSection` instead of using the + /// default. + pub fn lock_overrides<'b, CS>(&self, cs: &CS, f: impl FnOnce(&HandlerArray<'b>)) + where + CS: CriticalSection, + { + // Create a shorter-lived array from `self` that matches the + // lifetime of `f` so we can make sure `register` is only + // called with closures that will live as long as `f` does. + // + // Unsafe: This requires that we back up and restore the handlers + // in the array to make sure there's always something alive in + // whatever the real scope of `array' is. + let tmp: &HandlerArray<'b> = unsafe { core::mem::transmute(self) }; + + // Back up old handlers before entering inner scope so we can + // restore them on exit. + let bk = HandlerArray::new(); + unsafe { core::ptr::copy_nonoverlapping(tmp.h.get(), bk.h.get(), 1) } + f(tmp); + + // Put the old handlers back inside a critical section to avoid + // data races. + cs.with_lock(|| unsafe { core::ptr::copy_nonoverlapping(bk.h.get(), tmp.h.get(), 1) }); + } +} + +// Unsafe: as long as `register` and `with_overrides` use critical +// sections appropriately, it should be safe to share this between +// threads. +unsafe impl<'a> Sync for HandlerArray<'a> {} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn overrides_unwind() { + static mut CALLS: usize = 0; + let mut cl = || unsafe { CALLS += 1 }; + let cl_ref = &mut cl; + + let ht = HandlerArray::new(); + ht.with_overrides(|t| { + t.register(0, cl_ref); + ht.call(0); + }); + unsafe { assert_eq!(CALLS, 1) }; + ht.call(0); + unsafe { assert_eq!(CALLS, 1) }; + } +} |