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
//! Routines for managing interrupts.
//! Based on:
//! - <https://github.com/avr-rust/ruduino/blob/master/src/interrupt.rs>
//! - <https://docs.rs/bare-metal/0.2.5/src/bare_metal/lib.rs.html>
use core::{arch::asm, marker::PhantomData};
pub mod mutex;
use mutex::Mutex;
/// Atomic counter of critical sections to avoid problems when `without_interrupts` is used in
/// nested function calls.
#[cfg(not(feature = "unsafe-no-critical-section-count"))]
static CRITICAL_SECTION_COUNTER: Mutex<usize> = Mutex::new(0);
/// Helper struct that automatically restores interrupts on drop. The wrapped `PhantomData` creates
/// a private field to ensure that this struct cannot be initialized from outside of this module
/// without using its `unsafe` initializer function `new`. The recommended use to enter a
/// `CriticalSection` is to pass a closure to `without_interrupts`.
///
/// When the feature `unsafe-no-critical-section-count` is disabled, this implementation is also
/// safe w.r.t. nested calls of `without_interrupts`. This is achieved by counting how many
/// `CriticalSection`s were entered, and only enabling device interrupts once the last
/// `CriticalSection` is exited. However, as these checks incur a small runtime overhead, they can
/// be disabled with the feature `unsafe-no-critical-section-count`. Note that, for execution
/// consistency, a user must then ensure that `without_interrupts` will never be nested!
pub struct CriticalSection(PhantomData<()>);
impl CriticalSection {
/// Upon entering any `CriticalSection`, disable global device interrupts.
///
/// # Safety
/// When the feature `unsafe-no-critical-section-count` is disabled, this implementation is also
/// safe w.r.t. nested `CriticalSection`s, e.g., by nesting calls to `without_interrupts`. This
/// is achieved by counting how many `CriticalSection`s were entered, and only enabling device
/// interrupts once the last `CriticalSection` is exited. However, as these checks incur a small
/// runtime overhead, they can be disabled with the feature `unsafe-no-critical-section-count`.
/// Note that, for execution consistency, a user must then ensure that `CriticalSection`s will
/// never be nested!
#[inline(always)]
pub unsafe fn new() -> Self {
// first, deactivate interrupts
asm!("CLI");
// next, create the new `CriticalSection`
let cs = CriticalSection(PhantomData);
// now, in guaranteed single-threaded mode, increase number of `CriticalSection`s
#[cfg(not(feature = "unsafe-no-critical-section-count"))]
CRITICAL_SECTION_COUNTER.lock(&cs).update(|x| x + 1);
cs
}
}
impl Drop for CriticalSection {
/// Upon dropping the last `CriticalSection`, enable global device interrupts.
#[inline(always)]
fn drop(&mut self) {
#[cfg(not(feature = "unsafe-no-critical-section-count"))]
CRITICAL_SECTION_COUNTER.lock(self).update(|x| {
if x == 1 {
unsafe { asm!("SEI") }
}
x - 1
});
#[cfg(feature = "unsafe-no-critical-section-count")]
unsafe {
asm!("SEI")
}
}
}
/// Executes a closure, disabling interrupts until its completion. Introduces a `CriticalSection`
/// that allows to access shared data structures via the guards provided in the `mutex` module.
///
/// Restores interrupts after the closure has completed execution.
#[inline(always)]
pub fn without_interrupts<F, T>(f: F) -> T
where
F: FnOnce(&mut CriticalSection) -> T,
{
// entering a `CriticalSection` is unsafe
let mut critical_section = unsafe { CriticalSection::new() };
// run the given closure with a unique reference to the `CriticalSection` to allow
// accessing a `Mutex`
let result = f(&mut critical_section);
// explicitly ensure that the `CriticalSection` is left after the closure has been processed
drop(critical_section);
// return whatever the closure yielded
result
}