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/lib.rs | |
parent | f12811a0a5e15b596a0cc06c095832a6b795172b (diff) | |
download | clint-0.2.0.tar.gz clint-0.2.0.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/lib.rs')
-rw-r--r--[-rwxr-xr-x] | src/lib.rs | 218 |
1 files changed, 17 insertions, 201 deletions
diff --git a/src/lib.rs b/src/lib.rs index 39cbfb0..8e16b26 100755..100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,209 +1,25 @@ -#![no_std] -#![feature(const_fn)] -#![feature(test)] - -//! Call closures from interrupt handlers. -//! -//! # Motivation -//! -//! Existing solutions for interrupt handlers typically revolve around -//! wrapping resources needed by the handler in an `Option`, wrapped -//! in a `RefCell` wrapped in an `Mutex`, incurring some run-time -//! overhead every time the resource is required in the interrupt -//! handler, in addition to a fair amount of boilerplate. This module -//! attempts to leverage Rust's borrow checker and move semantics to -//! allow interrupt handlers to directly use their resources with a -//! minimum of overhead. -//! -//! To accomplish this, we use a closure which is called by an -//! interrupt handler. Because the closure has access to its -//! environment, we can use `move`, references, and mutable references -//! to ensure that variables are available as necessary to the -//! interrupt handler, while leveraging the borrow checker to ensure -//! safety at compile time. The only overhead is what it takes to call -//! the closure itself. -//! -//! # Safety +//! CLosure INTerrupt handlers //! -//! While this module endeavors to use Rust's safety guarantees to -//! allow for use of resources inside interrupt handlers, due to -//! expected use-cases, closure semantics, and how interrupt handlers -//! are invoked from hardware, certain operations cannot be done -//! safely at this level. +//! Use closures as interrupt service routines to leverage Rust's +//! borrow checker for safe, exclusive usage of device peripherals and +//! other data without locking. //! -//! Notably, for the handler to be useful when called from interrupt -//! context, it needs to be stored in a `static mut` variable. This -//! means that the closure you supply it must also be effectively -//! `static` or replaced with a longer-lived closure before it goes -//! out of scope. `Handler::default_handler()` is provided for this -//! purpose. +//! # Layout //! -//! Additionally, replacement of an interrupt handler's closure may -//! race with the calling of the interrupt handler's closure (i.e., -//! `Handler.replace()` may happen concurrently with -//! `Handler.call()`). You need to avoid this situation however is -//! appropriate for your code. The expected usage would be replacing -//! the handler`s closure once, while interrupts are disabled, thus -//! preventing the simultaneous replace/call problem. As this module -//! makes no assumptions about the environment in which it will be -//! used, this cannot be done for you. +//! See [`array`'s module documentation](table/index.html#examples) for basic, +//! safe usage. //! -//! # Examples +//! The [`handler`](handler) module contains the underyling, unsafe +//! implementation. //! -//! This example for an ARM Cortex-M system demonstrates safe usage by -//! only replacing the closure for `SYSTICK_HANDLER` inside a critical -//! section obtained by `cortex_m::interrupt::free()`, and shows how -//! it is called via the `SysTick()` function, which is called -//! directly from hardware. -//! -//! ``` no_run -//! use clint::Handler; -//! use cortex_m_rt::exception; -//! -//! static mut SYSTICK_HANDLER: Handler = Handler::new(); -//! -//! fn main() { -//! // NB: `closure` is in the lexical scope of `main`, and thus -//! // cannot go out of scope. -//! let closure = || { -//! // Your interrupt handling code. -//! }; -//! // Replace the handler for SysTick with closure while interrupts are -//! // disabled. -//! cortex_m::interrupt::free(|_| { -//! unsafe { SYSTICK_HANDLER.replace(&closure) }; -//! }); -//! -//! loop { -//! // Your main loop. -//! } -//! } -//! -//! #[exception] -//! fn SysTick() { -//! unsafe { SYSTICK_HANDLER.call() }; -//! } -//! ``` - -pub struct Handler { - // Handler that will be executed on `call`. - h: *const dyn FnMut(), -} - -impl Handler { - /// Returns a new Handler that initially does nothing when - /// called. Override it's behavior by using `replace`. - pub const fn new() -> Self { - Self { - h: &Self::default_handler, - } - } +//! Critical section support is supplied by the [`cs` module](cs). - /// Replace the behavior of this handler with `f`. - /// - /// # Safety - /// - /// There is no exclusion on replacing the handler's behavior - /// while it is being executed. It is your responsibility to make - /// sure that it's not being executed when you call `replace`. - pub unsafe fn replace<F>(&mut self, f: &F) - where - F: FnMut() + 'static, - { - self.h = f; - } - - /// Execute this handler. - /// - /// # Safety - /// - /// This function assumes that a replace is not occurring when the - /// closure is being looked up. You need to ensure that `replace` - /// and `call` can not occur at the same time. - pub unsafe fn call(&self) { - let f: &mut dyn FnMut() = &mut *(self.h as *mut dyn FnMut()); - f(); - } - - /// Do nothing handler. Needed by `call` until `replace` is used - /// to set specific behavior. Can also be used to replace a - /// closure that is about to go out of scope. - pub fn default_handler() {} -} - -impl core::fmt::Debug for Handler { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let (f0, f1) = unsafe { core::mem::transmute::<_, (usize, usize)>(self.h) }; - write!(f, "Handler{{ h: (0x{:x}, 0x{:x}) }}", f0, f1) - } -} - -#[cfg(test)] -mod test { - extern crate test; - use super::*; - use test::Bencher; - - #[test] - fn replace() { - static mut X: usize = 0; - - let mut handler = Handler::new(); - unsafe { - handler.replace(&|| X += 1); - assert_eq!(X, 0); - handler.call(); - handler.call(); - assert_eq!(X, 2); - } - } - - #[test] - fn replace_static() { - static mut HANDLER: Handler = Handler::new(); - static mut X: usize = 0; - - unsafe { - HANDLER.replace(&|| X += 1); - assert_eq!(X, 0); - HANDLER.call(); - HANDLER.call(); - assert_eq!(X, 2); - } - } - - #[test] - fn replace_with_default() { - let mut handler = Handler::new(); - unsafe { - handler.replace(&Handler::default_handler); - handler.call() - } - } - - const ITER_COUNT: usize = 10_000; - - #[bench] - fn bench_bare_fn(b: &mut Bencher) { - static mut X: usize = 0; - #[inline(never)] - fn inc() { - unsafe { X += 1 }; - } - - let n = test::black_box(ITER_COUNT); - b.iter(|| (0..n).for_each(|_| inc())); - assert!(unsafe { X } > 0); - } +#![no_std] +#![feature(const_fn)] - #[bench] - fn bench_handler(b: &mut Bencher) { - static mut X: usize = 0; - let mut handler = Handler::new(); - unsafe { handler.replace(&move || X += 1) }; +pub mod array; +pub mod cs; +pub mod handler; - let n = test::black_box(ITER_COUNT); - b.iter(|| (0..n).for_each(|_| unsafe { handler.call() })); - assert!(unsafe { X } > 0); - } -} +pub use array::HandlerArray; +pub use handler::Handler; |