clint 0.3.0

CLosure INTerrupt handlers.
Documentation
//! 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
//!
//! 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.
//!
//! 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.
//!
//! 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.
//!
//! # Examples
//!
//! 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<'a> {
    // Handler that will be executed on `call`.
    h: *const dyn FnMut(),
    lifetime: core::marker::PhantomData<&'a dyn FnMut()>,
}

impl<'a> Handler<'a> {
    /// Returns a new Handler that initially does nothing when
    /// called. Override its behavior by using `replace`.
    pub const fn new() -> Self {
        Self {
            h: &Self::default_handler,
            lifetime: core::marker::PhantomData,
        }
    }

    /// 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(&mut self, f: &(dyn FnMut() + Send + 'a)) {
        self.h = core::mem::transmute::<_, &'a _>(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<'a> core::fmt::Debug for Handler<'a> {
    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)
    }
}

// FIXME: This probably shouldn't be Copy/Clone, but it needs to be in
// order for array initialization to work with [Handler::new(); 32].
impl<'a> core::marker::Copy for Handler<'a> {}
impl<'a> core::clone::Clone for Handler<'a> {
    fn clone(&self) -> Self {
        *self
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[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()
        }
    }
}