aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib.rs
blob: 39cbfb0c609a80ac19c9de5af0f88cc22ada9e79 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#![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
//!
//! 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 {
    // 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,
        }
    }

    /// 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);
    }

    #[bench]
    fn bench_handler(b: &mut Bencher) {
        static mut X: usize = 0;
        let mut handler = Handler::new();
        unsafe { handler.replace(&move || X += 1) };

        let n = test::black_box(ITER_COUNT);
        b.iter(|| (0..n).for_each(|_| unsafe { handler.call() }));
        assert!(unsafe { X } > 0);
    }
}