@@ -6,7 +6,6 @@ use embassy_executor::Spawner;
66#[ cfg( all( low_power_wait, multi_core) ) ]
77use esp_hal:: interrupt:: software:: SoftwareInterrupt ;
88use esp_hal:: { interrupt:: Priority , system:: Cpu } ;
9- #[ cfg( low_power_wait) ]
109use portable_atomic:: { AtomicBool , Ordering } ;
1110
1211use super :: InnerExecutor ;
@@ -15,28 +14,36 @@ pub(crate) const THREAD_MODE_CONTEXT: usize = 16;
1514
1615/// global atomic used to keep track of whether there is work to do since sev()
1716/// is not available on either Xtensa or RISC-V
18- #[ cfg( low_power_wait) ]
1917static SIGNAL_WORK_THREAD_MODE : [ AtomicBool ; Cpu :: COUNT ] =
2018 [ const { AtomicBool :: new ( false ) } ; Cpu :: COUNT ] ;
2119
22- pub ( crate ) fn pend_thread_mode ( _core : usize ) {
23- #[ cfg( low_power_wait) ]
24- {
25- // Signal that there is work to be done.
26- SIGNAL_WORK_THREAD_MODE [ _core] . store ( true , Ordering :: Relaxed ) ;
27-
28- // If we are pending a task on the current core, we're done. Otherwise, we
29- // need to make sure the other core wakes up.
30- #[ cfg( multi_core) ]
31- if _core != Cpu :: current ( ) as usize {
32- // We need to clear the interrupt from software. We don't actually
33- // need it to trigger and run the interrupt handler, we just need to
34- // kick waiti to return.
35- unsafe { SoftwareInterrupt :: < 3 > :: steal ( ) . raise ( ) } ;
36- }
20+ pub ( crate ) fn pend_thread_mode ( core : usize ) {
21+ // Signal that there is work to be done.
22+ SIGNAL_WORK_THREAD_MODE [ core] . store ( true , Ordering :: Relaxed ) ;
23+
24+ // If we are pending a task on the current core, we're done. Otherwise, we
25+ // need to make sure the other core wakes up.
26+ #[ cfg( all( low_power_wait, multi_core) ) ]
27+ if core != Cpu :: current ( ) as usize {
28+ // We need to clear the interrupt from software. We don't actually
29+ // need it to trigger and run the interrupt handler, we just need to
30+ // kick waiti to return.
31+ unsafe { SoftwareInterrupt :: < 3 > :: steal ( ) . raise ( ) } ;
3732 }
3833}
3934
35+ /// Callbacks to run code before/after polling the task queue.
36+ pub trait Callbacks {
37+ /// Called just before polling the executor.
38+ fn before_poll ( & mut self ) ;
39+
40+ /// Called after the executor is polled, if there is no work scheduled.
41+ ///
42+ /// Note that tasks can become ready at any point during the execution
43+ /// of this function.
44+ fn on_idle ( & mut self ) ;
45+ }
46+
4047/// Thread mode executor.
4148///
4249/// This is the simplest and most common kind of executor. It runs on thread
@@ -49,6 +56,7 @@ create one instance per core. The executors don't steal tasks from each other."
4956) ]
5057pub struct Executor {
5158 inner : InnerExecutor ,
59+ cpu : Cpu ,
5260 not_send : PhantomData < * mut ( ) > ,
5361}
5462
@@ -61,14 +69,16 @@ impl Executor {
6169This will use software-interrupt 3 which isn't available for anything else to wake the other core(s)."#
6270 ) ]
6371 pub fn new ( ) -> Self {
72+ let cpu = Cpu :: current ( ) ;
6473 Self {
6574 inner : InnerExecutor :: new (
6675 // Priority 1 means the timer queue can be accessed at interrupt priority 1 - for
6776 // the thread mode executor it needs to be one higher than the base run level, to
6877 // allow alarm interrupts to be handled.
6978 Priority :: Priority1 ,
70- ( THREAD_MODE_CONTEXT + Cpu :: current ( ) as usize ) as * mut ( ) ,
79+ ( THREAD_MODE_CONTEXT + cpu as usize ) as * mut ( ) ,
7180 ) ,
81+ cpu,
7282 not_send : PhantomData ,
7383 }
7484 }
@@ -94,6 +104,58 @@ This will use software-interrupt 3 which isn't available for anything else to wa
94104 ///
95105 /// This function never returns.
96106 pub fn run ( & ' static mut self , init : impl FnOnce ( Spawner ) ) -> ! {
107+ struct NoHooks ( usize ) ;
108+
109+ impl Callbacks for NoHooks {
110+ fn before_poll ( & mut self ) {
111+ #[ cfg( low_power_wait) ]
112+ SIGNAL_WORK_THREAD_MODE [ self . 0 ] . store ( false , Ordering :: Relaxed ) ;
113+ }
114+
115+ fn on_idle ( & mut self ) { }
116+ }
117+
118+ self . run_inner ( init, NoHooks ( self . cpu as usize ) )
119+ }
120+
121+ /// Run the executor with callbacks.
122+ ///
123+ /// See [Callbacks] on when the callbacks are called.
124+ ///
125+ /// See [Self::run] for more information about running the executor.
126+ ///
127+ /// This function never returns.
128+ pub fn run_with_callbacks (
129+ & ' static mut self ,
130+ init : impl FnOnce ( Spawner ) ,
131+ callbacks : impl Callbacks ,
132+ ) -> ! {
133+ struct Hooks < ' a , CB : Callbacks > ( CB , & ' a AtomicBool ) ;
134+
135+ impl < CB : Callbacks > Callbacks for Hooks < ' _ , CB > {
136+ fn before_poll ( & mut self ) {
137+ // Clear the flag unconditionally since we'll use it to decide
138+ // if on_idle should be called.
139+ self . 1 . store ( false , Ordering :: Relaxed ) ;
140+
141+ self . 0 . before_poll ( )
142+ }
143+
144+ fn on_idle ( & mut self ) {
145+ // Make sure we only call on_idle if the executor would otherwise go to sleep.
146+ if !self . 1 . load ( Ordering :: Acquire ) {
147+ self . 0 . on_idle ( ) ;
148+ }
149+ }
150+ }
151+
152+ self . run_inner (
153+ init,
154+ Hooks ( callbacks, & SIGNAL_WORK_THREAD_MODE [ self . cpu as usize ] ) ,
155+ )
156+ }
157+
158+ fn run_inner ( & ' static mut self , init : impl FnOnce ( Spawner ) , mut hooks : impl Callbacks ) -> ! {
97159 #[ cfg( all( multi_core, low_power_wait) ) ]
98160 unwrap ! ( esp_hal:: interrupt:: enable(
99161 esp_hal:: peripherals:: Interrupt :: FROM_CPU_INTR3 ,
@@ -104,14 +166,15 @@ This will use software-interrupt 3 which isn't available for anything else to wa
104166
105167 init ( self . inner . inner . spawner ( ) ) ;
106168
107- #[ cfg( low_power_wait) ]
108- let cpu = Cpu :: current ( ) as usize ;
109-
110169 loop {
170+ hooks. before_poll ( ) ;
171+
111172 unsafe { self . inner . inner . poll ( ) } ;
112173
174+ hooks. on_idle ( ) ;
175+
113176 #[ cfg( low_power_wait) ]
114- Self :: wait_impl ( cpu) ;
177+ Self :: wait_impl ( self . cpu as usize ) ;
115178 }
116179 }
117180
@@ -154,8 +217,6 @@ This will use software-interrupt 3 which isn't available for anything else to wa
154217 _ => unsafe { core:: arch:: asm!( "waiti 5" ) } ,
155218 }
156219 }
157- // If this races and some waker sets the signal, we'll reset it, but still poll.
158- SIGNAL_WORK_THREAD_MODE [ cpu] . store ( false , Ordering :: Relaxed ) ;
159220 }
160221
161222 #[ cfg( all( riscv, low_power_wait) ) ]
@@ -169,9 +230,6 @@ This will use software-interrupt 3 which isn't available for anything else to wa
169230 unsafe { core:: arch:: asm!( "wfi" ) } ;
170231 }
171232 } ) ;
172- // if an interrupt occurred while waiting, it will be serviced here
173- // If this races and some waker sets the signal, we'll reset it, but still poll.
174- SIGNAL_WORK_THREAD_MODE [ cpu] . store ( false , Ordering :: Relaxed ) ;
175233 }
176234}
177235
0 commit comments