1use core::{cell::UnsafeCell, mem::MaybeUninit, sync::atomic::Ordering};
4
5use embassy_executor::{SendSpawner, Spawner, raw};
6use esp_hal::interrupt::{InterruptHandler, Priority, software::SoftwareInterrupt};
7#[cfg(multi_core)]
8use esp_hal::system::Cpu;
9use macros::ram;
10use portable_atomic::AtomicPtr;
11
12use crate::task::flags::ThreadFlags;
13
14#[unsafe(export_name = "__pender")]
15#[ram]
16fn __pender(context: *mut ()) {
17 match context as usize {
18 0 => unsafe { SoftwareInterrupt::<0>::steal().raise() },
19 1 => unsafe { SoftwareInterrupt::<1>::steal().raise() },
20 2 => unsafe { SoftwareInterrupt::<2>::steal().raise() },
21 3 => unsafe { SoftwareInterrupt::<3>::steal().raise() },
22 _ => {
23 let flags = unwrap!(unsafe { context.cast::<ThreadFlags>().as_ref() });
26 flags.set(1);
27 }
28 }
29}
30
31pub trait Callbacks {
33 fn before_poll(&mut self);
35
36 fn on_idle(&mut self);
41}
42
43#[cfg_attr(
48 multi_core,
49 doc = r"
50
51If you want to start the executor on the second core, you will need to start the second core using [`crate::start_second_core`].
52If you are looking for a way to run code on the second core without the scheduler, use the [`InterruptExecutor`].
53"
54)]
55pub struct Executor {
56 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
57}
58
59impl Executor {
60 pub const fn new() -> Self {
62 Self {
63 executor: UnsafeCell::new(MaybeUninit::uninit()),
64 }
65 }
66
67 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
88 let flags = ThreadFlags::new();
89 struct NoHooks;
90
91 impl Callbacks for NoHooks {
92 fn before_poll(&mut self) {}
93
94 fn on_idle(&mut self) {}
95 }
96
97 self.run_inner(init, &flags, NoHooks)
98 }
99
100 pub fn run_with_callbacks(
108 &'static mut self,
109 init: impl FnOnce(Spawner),
110 callbacks: impl Callbacks,
111 ) -> ! {
112 let flags = ThreadFlags::new();
113 struct Hooks<'a, CB: Callbacks>(CB, &'a ThreadFlags);
114
115 impl<CB: Callbacks> Callbacks for Hooks<'_, CB> {
116 fn before_poll(&mut self) {
117 self.0.before_poll()
118 }
119
120 fn on_idle(&mut self) {
121 if self.1.get() == 0 {
123 self.0.on_idle();
124 }
125 }
126 }
127
128 self.run_inner(init, &flags, Hooks(callbacks, &flags))
129 }
130
131 fn run_inner(
132 &'static self,
133 init: impl FnOnce(Spawner),
134 flags: &ThreadFlags,
135 mut hooks: impl Callbacks,
136 ) -> ! {
137 let executor = unsafe {
138 (&mut *self.executor.get()).write(raw::Executor::new(
139 (flags as *const ThreadFlags).cast::<()>().cast_mut(),
140 ))
141 };
142
143 #[cfg(multi_core)]
144 if Cpu::current() != Cpu::ProCpu
145 && crate::SCHEDULER
146 .with(|scheduler| !scheduler.per_cpu[Cpu::current() as usize].initialized)
147 {
148 panic!("Executor cannot be started: the scheduler is not running on the current CPU.");
149 }
150
151 init(executor.spawner());
152
153 loop {
154 hooks.before_poll();
155
156 unsafe { executor.poll() };
157
158 hooks.on_idle();
159
160 flags.wait(1, None);
162 }
163 }
164}
165
166impl Default for Executor {
167 fn default() -> Self {
168 Self::new()
169 }
170}
171
172pub struct InterruptExecutor<const SWI: u8> {
181 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
182 interrupt: SoftwareInterrupt<'static, SWI>,
183}
184
185const COUNT: usize = 4;
186static INTERRUPT_EXECUTORS: [InterruptExecutorStorage; COUNT] =
187 [const { InterruptExecutorStorage::new() }; COUNT];
188
189unsafe impl<const SWI: u8> Send for InterruptExecutor<SWI> {}
190unsafe impl<const SWI: u8> Sync for InterruptExecutor<SWI> {}
191
192struct InterruptExecutorStorage {
193 raw_executor: AtomicPtr<raw::Executor>,
194}
195
196impl InterruptExecutorStorage {
197 const fn new() -> Self {
198 Self {
199 raw_executor: AtomicPtr::new(core::ptr::null_mut()),
200 }
201 }
202
203 unsafe fn get(&self) -> &raw::Executor {
207 unsafe { &*self.raw_executor.load(Ordering::Relaxed) }
208 }
209
210 fn set(&self, executor: *mut raw::Executor) {
211 self.raw_executor.store(executor, Ordering::Relaxed);
212 }
213}
214
215extern "C" fn handle_interrupt<const NUM: u8>() {
216 let swi = unsafe { SoftwareInterrupt::<NUM>::steal() };
217 swi.reset();
218
219 unsafe {
220 let executor = INTERRUPT_EXECUTORS[NUM as usize].get();
222 executor.poll();
223 }
224}
225
226impl<const SWI: u8> InterruptExecutor<SWI> {
227 #[inline]
230 pub const fn new(interrupt: SoftwareInterrupt<'static, SWI>) -> Self {
231 Self {
232 executor: UnsafeCell::new(MaybeUninit::uninit()),
233 interrupt,
234 }
235 }
236
237 pub fn start(&'static mut self, priority: Priority) -> SendSpawner {
250 unsafe {
251 (*self.executor.get()).write(raw::Executor::new((SWI as usize) as *mut ()));
252
253 INTERRUPT_EXECUTORS[SWI as usize].set((*self.executor.get()).as_mut_ptr());
254 }
255
256 let swi_handler = match SWI {
257 0 => handle_interrupt::<0>,
258 1 => handle_interrupt::<1>,
259 2 => handle_interrupt::<2>,
260 3 => handle_interrupt::<3>,
261 _ => unreachable!(),
262 };
263
264 self.interrupt
265 .set_interrupt_handler(InterruptHandler::new(swi_handler, priority));
266
267 let executor = unsafe { (*self.executor.get()).assume_init_ref() };
268 executor.spawner().make_send()
269 }
270
271 pub fn spawner(&'static self) -> SendSpawner {
279 if INTERRUPT_EXECUTORS[SWI as usize]
280 .raw_executor
281 .load(Ordering::Acquire)
282 .is_null()
283 {
284 panic!("InterruptExecutor::spawner() called on uninitialized executor.");
285 }
286 let executor = unsafe { (*self.executor.get()).assume_init_ref() };
287 executor.spawner().make_send()
288 }
289}