aboutsummaryrefslogtreecommitdiffstats
path: root/src/array.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/array.rs')
-rw-r--r--src/array.rs171
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) };
+ }
+}