use core::{cell::UnsafeCell, mem::MaybeUninit};
use embassy_executor::SendSpawner;
use esp_hal::{
interrupt::{self, InterruptHandler, software::SoftwareInterrupt},
system::Cpu,
};
use portable_atomic::{AtomicUsize, Ordering};
use super::InnerExecutor;
const COUNT: usize = 3 + cfg!(not(multi_core)) as usize;
static mut EXECUTORS: [CallbackContext; COUNT] = [const { CallbackContext::new() }; COUNT];
pub struct InterruptExecutor<const SWI: u8> {
core: AtomicUsize,
executor: UnsafeCell<MaybeUninit<InnerExecutor>>,
interrupt: SoftwareInterrupt<'static, SWI>,
}
unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}
struct CallbackContext {
raw_executor: UnsafeCell<*mut InnerExecutor>,
}
impl CallbackContext {
const fn new() -> Self {
Self {
raw_executor: UnsafeCell::new(core::ptr::null_mut()),
}
}
unsafe fn get(&self) -> &InnerExecutor {
unsafe { &**self.raw_executor.get() }
}
fn set(&self, executor: *mut InnerExecutor) {
unsafe { self.raw_executor.get().write(executor) };
}
}
extern "C" fn handle_interrupt<const NUM: u8>() {
let swi = unsafe { SoftwareInterrupt::<NUM>::steal() };
swi.reset();
unsafe {
let executor = EXECUTORS[NUM as usize].get();
executor.inner.poll();
}
}
impl<const SWI: u8> InterruptExecutor<SWI> {
#[inline]
pub const fn new(interrupt: SoftwareInterrupt<'static, SWI>) -> Self {
Self {
core: AtomicUsize::new(usize::MAX),
executor: UnsafeCell::new(MaybeUninit::uninit()),
interrupt,
}
}
pub fn start(&'static mut self, priority: interrupt::Priority) -> SendSpawner {
if self
.core
.compare_exchange(
usize::MAX,
Cpu::current() as usize,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_err()
{
panic!("InterruptExecutor::start() called multiple times on the same executor.");
}
unsafe {
(*self.executor.get())
.as_mut_ptr()
.write(InnerExecutor::new(priority, (SWI as usize) as *mut ()));
EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
}
let swi_handler = match SWI {
0 => handle_interrupt::<0>,
1 => handle_interrupt::<1>,
2 => handle_interrupt::<2>,
#[cfg(not(multi_core))]
3 => handle_interrupt::<3>,
_ => unreachable!(),
};
self.interrupt
.set_interrupt_handler(InterruptHandler::new(swi_handler, priority));
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.init();
executor.inner.spawner().make_send()
}
pub fn spawner(&'static self) -> SendSpawner {
if self.core.load(Ordering::Acquire) == usize::MAX {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (*self.executor.get()).assume_init_ref() };
executor.inner.spawner().make_send()
}
}