Repository: embassy-rs/embassy
Pull request #3547 (2024-11-19 13:40:27)

Executor: Only set callback once
Untested. Let me know if I shouldn't waste time here because this was done for a reason.


Patch:
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index e07e3924f..00b1bef28 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## Unreleased
 
+- `raw::Executor` now has an `fn initialize` that must be called once before starting to poll it.
+
 ## 0.6.3 - 2024-11-12
 
 - Building with the `nightly` feature now works with the Xtensa Rust compiler 1.82.
diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs
index 70085d04d..7f9ed4421 100644
--- a/embassy-executor/src/arch/avr.rs
+++ b/embassy-executor/src/arch/avr.rs
@@ -53,6 +53,10 @@ mod thread {
         ///
         /// This function never returns.
         pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
+            unsafe {
+                self.inner.initialize();
+            }
+
             init(self.inner.spawner());
 
             loop {
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 5c517e0a2..0c2af88a6 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -98,6 +98,9 @@ mod thread {
         ///
         /// This function never returns.
         pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
+            unsafe {
+                self.inner.initialize();
+            }
             init(self.inner.spawner());
 
             loop {
@@ -207,6 +210,9 @@ mod interrupt {
             }
 
             let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
+            unsafe {
+                executor.initialize();
+            }
 
             unsafe { NVIC::unmask(irq) }
 
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index 01e63a9fd..715e5f3cf 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -54,6 +54,10 @@ mod thread {
         ///
         /// This function never returns.
         pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
+            unsafe {
+                self.inner.initialize();
+            }
+
             init(self.inner.spawner());
 
             loop {
diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs
index 340023620..54c7458b3 100644
--- a/embassy-executor/src/arch/spin.rs
+++ b/embassy-executor/src/arch/spin.rs
@@ -48,6 +48,10 @@ mod thread {
         ///
         /// This function never returns.
         pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
+            unsafe {
+                self.inner.initialize();
+            }
+
             init(self.inner.spawner());
 
             loop {
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index b02b15988..948c7711b 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -55,6 +55,10 @@ mod thread {
         ///
         /// This function never returns.
         pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
+            unsafe {
+                self.inner.initialize();
+            }
+
             init(self.inner.spawner());
 
             loop {
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index f9d0f935c..35025f11f 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -70,6 +70,10 @@ mod thread {
         /// - a `static mut` (unsafe)
         /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
         pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
+            unsafe {
+                self.inner.initialize();
+            }
+
             unsafe {
                 let executor = &self.inner;
                 let future = Closure::new(move |_| {
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index d9ea5c005..ebabee1ba 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -339,6 +339,11 @@ impl SyncExecutor {
         }
     }
 
+    pub(crate) unsafe fn initialize(&'static self) {
+        #[cfg(feature = "integrated-timers")]
+        embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
+    }
+
     /// Enqueue a task in the task queue
     ///
     /// # Safety
@@ -374,9 +379,6 @@ impl SyncExecutor {
     ///
     /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
     pub(crate) unsafe fn poll(&'static self) {
-        #[cfg(feature = "integrated-timers")]
-        embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
-
         #[allow(clippy::never_loop)]
         loop {
             #[cfg(feature = "integrated-timers")]
@@ -492,6 +494,15 @@ impl Executor {
         }
     }
 
+    /// Initializes the executor.
+    ///
+    /// # Safety
+    ///
+    /// This function must be called once before any other method is called.
+    pub unsafe fn initialize(&'static self) {
+        self.inner.initialize();
+    }
+
     /// Spawn a task in this executor.
     ///
     /// # Safety
@@ -516,6 +527,8 @@ impl Executor {
     ///
     /// # Safety
     ///
+    /// You must call `initialize` before calling this method.
+    ///
     /// You must NOT call `poll` reentrantly on the same executor.
     ///
     /// In particular, note that `poll` may call the pender synchronously. Therefore, you
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index a779b8a09..d74221864 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -155,7 +155,9 @@ impl Executor {
                 time_driver: get_driver(),
             });
 
-            EXECUTOR.as_mut().unwrap()
+            let executor = EXECUTOR.as_mut().unwrap();
+
+            executor
         })
     }
 
@@ -241,11 +243,15 @@ impl Executor {
     ///
     /// This function never returns.
     pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
-        init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner());
+        let executor = unsafe { EXECUTOR.as_mut().unwrap() };
+        unsafe {
+            executor.inner.initialize();
+        }
+        init(executor.inner.spawner());
 
         loop {
             unsafe {
-                EXECUTOR.as_mut().unwrap().inner.poll();
+                executor.inner.poll();
                 self.configure_pwr();
                 asm!("wfe");
             };


Test patch:
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs
index 348cc7dc4..8054bf7eb 100644
--- a/embassy-executor/tests/test.rs
+++ b/embassy-executor/tests/test.rs
@@ -40,6 +40,10 @@ fn setup() -> (&'static Executor, Trace) {
     let trace = Trace::new();
     let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
     let executor = &*Box::leak(Box::new(Executor::new(context)));
+    unsafe {
+        executor.initialize();
+    }
+
     (executor, trace)
 }