#![doc(html_root_url = "https://docs.rs/irq/0.2.3")]
#![doc(test(attr(deny(unused_imports, unused_must_use))))]
#![warn(missing_debug_implementations, rust_2018_idioms)]
#![cfg_attr(not(test), no_std)]
mod lock;
mod readme;
pub use lock::*;
use core::fmt;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicUsize, Ordering};
#[macro_export]
macro_rules! scoped_interrupts {
(
$( #[$enum_attr:meta] )*
$v:vis enum $name:ident {
$(
$interrupt:ident
),+
$(,)?
}
use #[$hook_attr:meta];
) => {
$( #[$enum_attr] )*
$v enum $name {
$(
$interrupt,
)+
}
pub(crate) mod statics {
$(
#[allow(bad_style)]
pub(crate) static $interrupt: $crate::HandlerAddr = $crate::HandlerAddr::new();
)+
}
$(
#[$hook_attr]
#[allow(bad_style, dead_code)]
unsafe fn $interrupt() {
let handler = self::statics::$interrupt.load();
if handler == 0 {
if cfg!(debug_assertions) {
panic!(concat!(
"no handler registered for ",
::core::stringify!($interrupt)
));
} else {
while self::statics::$interrupt.load() == 0 {}
}
} else {
let handler = handler as *mut $crate::Handler<'_>;
(*handler).invoke();
}
}
)+
unsafe impl $crate::Interrupt for $name {
unsafe fn register(self, handler: &mut $crate::Handler<'_>) {
match self {
$(
Self::$interrupt => {
self::statics::$interrupt.store(handler as *mut _ as usize);
}
)+
}
}
fn deregister_all() {
unsafe {
$(
self::statics::$interrupt.store(0);
)+
}
}
}
};
}
#[macro_export]
macro_rules! handler {
($name:ident = $e:expr) => {
let mut closure = $e;
let $name = &mut $crate::Handler::new(&mut closure);
};
}
pub fn scope<'env, I, F, R>(f: F) -> R
where
I: Interrupt,
F: FnOnce(&Scope<'env, I>) -> R,
{
let scope = Scope { _p: PhantomData };
let result = f(&scope);
drop(scope);
result
}
#[allow(missing_debug_implementations)]
pub struct Scope<'env, I: Interrupt> {
_p: PhantomData<(I, &'env mut &'env ())>,
}
impl<'env, I: Interrupt> Scope<'env, I> {
#[inline]
pub fn register(&self, interrupt: I, handler: &'env mut Handler<'env>) {
unsafe {
interrupt.register(handler);
}
}
}
impl<'env, I: Interrupt> Drop for Scope<'env, I> {
fn drop(&mut self) {
I::deregister_all();
}
}
pub struct Handler<'a> {
f: &'a mut dyn FnMut(),
}
impl<'a> Handler<'a> {
#[inline(always)]
pub fn new<F>(f: &'a mut F) -> Self
where
F: FnMut() + Send + 'a,
{
Self { f }
}
#[inline(always)]
pub fn invoke(&mut self) {
(self.f)();
}
}
impl<'a> fmt::Debug for Handler<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "handler@{:p}", self as *const _)
}
}
#[doc(hidden)]
pub struct HandlerAddr {
addr: AtomicUsize,
}
impl HandlerAddr {
#[inline(always)]
pub const fn new() -> Self {
Self {
addr: AtomicUsize::new(0),
}
}
#[inline(always)]
pub fn load(&self) -> usize {
self.addr.load(Ordering::Acquire)
}
#[inline(always)]
pub unsafe fn store(&self, addr: usize) {
self.addr.store(addr, Ordering::Release)
}
}
impl fmt::Debug for HandlerAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "active handler@{:p}", self.load() as *const ())
}
}
pub unsafe trait Interrupt {
unsafe fn register(self, handler: &mut Handler<'_>);
fn deregister_all();
}
#[cfg(test)]
mod tests {
use super::Interrupt as _;
use super::*;
use std::panic::catch_unwind;
scoped_interrupts! {
enum Interrupt {
Int0,
Int1,
}
use #[no_mangle];
}
struct Test {}
#[derive(Debug)]
struct Panicked {}
impl Test {
fn raise_interrupt(&mut self, int: Interrupt) -> Result<(), Panicked> {
catch_unwind(|| match int {
Interrupt::Int0 => unsafe { Int0() },
Interrupt::Int1 => unsafe { Int1() },
})
.map_err(|_| Panicked {})
}
}
impl Drop for Test {
fn drop(&mut self) {
Interrupt::deregister_all();
}
}
fn test(f: impl FnOnce(&mut Test)) {
#[cfg(not(miri))]
let _guard = {
use once_cell::sync::OnceCell;
use std::sync::Mutex;
static MUTEX: OnceCell<Mutex<()>> = OnceCell::new();
MUTEX
.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(|e| e.into_inner()) };
let mut test = Test {};
f(&mut test);
}
#[test]
fn not_registered() {
test(|test| {
test.raise_interrupt(Interrupt::Int0).unwrap_err();
test.raise_interrupt(Interrupt::Int1).unwrap_err();
test.raise_interrupt(Interrupt::Int0).unwrap_err();
test.raise_interrupt(Interrupt::Int1).unwrap_err();
});
}
#[test]
fn simple() {
test(|test| {
let mut i = 0;
let mut closure = || {
i += 1;
};
let mut handler = Handler::new(&mut closure);
scope(|scope| {
scope.register(Interrupt::Int0, &mut handler);
test.raise_interrupt(Interrupt::Int0).unwrap();
});
assert_eq!(i, 1);
test.raise_interrupt(Interrupt::Int0).unwrap_err();
assert_eq!(i, 1);
});
}
#[test]
fn handler_sharing_data() {
test(|test| {
let shared = vec![0, 1, 2];
handler!(handler0 = || println!("{:?}", shared));
handler!(handler1 = || println!("{:?}", shared));
scope(|scope| {
scope.register(Interrupt::Int0, handler0);
scope.register(Interrupt::Int1, handler1);
test.raise_interrupt(Interrupt::Int0).unwrap();
test.raise_interrupt(Interrupt::Int1).unwrap();
});
})
}
}