executor: fix unsoundness in InterruptExecutor::start.

The initial closure is not actually called in the interrupt, so this is
illegally sending non-Send futures to the interrupt.

Remove the closure, and return a SendSpawner instead.
This commit is contained in:
Dario Nieuwenhuis 2022-04-25 22:09:04 +02:00
parent 52ed08cf95
commit b27feb0619
4 changed files with 22 additions and 28 deletions

View File

@ -1,7 +1,7 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ptr; use core::ptr;
use super::{raw, Spawner}; use super::{raw, SendSpawner, Spawner};
use crate::interrupt::{Interrupt, InterruptExt}; use crate::interrupt::{Interrupt, InterruptExt};
/// Thread mode executor, using WFE/SEV. /// Thread mode executor, using WFE/SEV.
@ -107,13 +107,13 @@ impl<I: Interrupt> InterruptExecutor<I> {
/// Start the executor. /// Start the executor.
/// ///
/// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on /// This initializes the executor, configures and enables the interrupt, and returns.
/// this executor. Use it to spawn the initial task(s). After `init` returns, /// The executor keeps running in the background through the interrupt.
/// the interrupt is configured so that the executor starts running the tasks.
/// Once the executor is started, `start` returns.
/// ///
/// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
/// for example by passing it as an argument to the initial tasks. /// is returned instead of a [`Spawner`] because the executor effectively runs in a
/// different "thread" (the interrupt), so spawning tasks on it is effectively
/// sending them.
/// ///
/// This function requires `&'static mut self`. This means you have to store the /// This function requires `&'static mut self`. This means you have to store the
/// Executor instance in a place where it'll live forever and grants you mutable /// Executor instance in a place where it'll live forever and grants you mutable
@ -122,16 +122,16 @@ impl<I: Interrupt> InterruptExecutor<I> {
/// - a [Forever](crate::util::Forever) (safe) /// - a [Forever](crate::util::Forever) (safe)
/// - a `static mut` (unsafe) /// - a `static mut` (unsafe)
/// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (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) + Send) { pub fn start(&'static mut self) -> SendSpawner {
self.irq.disable(); self.irq.disable();
init(self.inner.spawner());
self.irq.set_handler(|ctx| unsafe { self.irq.set_handler(|ctx| unsafe {
let executor = &*(ctx as *const raw::Executor); let executor = &*(ctx as *const raw::Executor);
executor.poll(); executor.poll();
}); });
self.irq.set_handler_context(&self.inner as *const _ as _); self.irq.set_handler_context(&self.inner as *const _ as _);
self.irq.enable(); self.irq.enable();
self.inner.spawner().make_send()
} }
} }

View File

@ -124,17 +124,15 @@ fn main() -> ! {
let irq = interrupt::take!(SWI1_EGU1); let irq = interrupt::take!(SWI1_EGU1);
irq.set_priority(interrupt::Priority::P6); irq.set_priority(interrupt::Priority::P6);
let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_high())); unwrap!(spawner.spawn(run_high()));
});
// Medium-priority executor: SWI0_EGU0, priority level 7 // Medium-priority executor: SWI0_EGU0, priority level 7
let irq = interrupt::take!(SWI0_EGU0); let irq = interrupt::take!(SWI0_EGU0);
irq.set_priority(interrupt::Priority::P7); irq.set_priority(interrupt::Priority::P7);
let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_med())); unwrap!(spawner.spawn(run_med()));
});
// Low priority executor: runs in thread mode, using WFE/SEV // Low priority executor: runs in thread mode, using WFE/SEV
let executor = EXECUTOR_LOW.put(Executor::new()); let executor = EXECUTOR_LOW.put(Executor::new());

View File

@ -124,17 +124,15 @@ fn main() -> ! {
let irq = interrupt::take!(UART4); let irq = interrupt::take!(UART4);
irq.set_priority(interrupt::Priority::P6); irq.set_priority(interrupt::Priority::P6);
let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_high())); unwrap!(spawner.spawn(run_high()));
});
// Medium-priority executor: SWI0_EGU0, priority level 7 // Medium-priority executor: SWI0_EGU0, priority level 7
let irq = interrupt::take!(UART5); let irq = interrupt::take!(UART5);
irq.set_priority(interrupt::Priority::P7); irq.set_priority(interrupt::Priority::P7);
let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_med())); unwrap!(spawner.spawn(run_med()));
});
// Low priority executor: runs in thread mode, using WFE/SEV // Low priority executor: runs in thread mode, using WFE/SEV
let executor = EXECUTOR_LOW.put(Executor::new()); let executor = EXECUTOR_LOW.put(Executor::new());

View File

@ -124,17 +124,15 @@ fn main() -> ! {
let irq = interrupt::take!(UART4); let irq = interrupt::take!(UART4);
irq.set_priority(interrupt::Priority::P6); irq.set_priority(interrupt::Priority::P6);
let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_HIGH.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_high())); unwrap!(spawner.spawn(run_high()));
});
// Medium-priority executor: SWI0_EGU0, priority level 7 // Medium-priority executor: SWI0_EGU0, priority level 7
let irq = interrupt::take!(UART5); let irq = interrupt::take!(UART5);
irq.set_priority(interrupt::Priority::P7); irq.set_priority(interrupt::Priority::P7);
let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq)); let executor = EXECUTOR_MED.put(InterruptExecutor::new(irq));
executor.start(|spawner| { let spawner = executor.start();
unwrap!(spawner.spawn(run_med())); unwrap!(spawner.spawn(run_med()));
});
// Low priority executor: runs in thread mode, using WFE/SEV // Low priority executor: runs in thread mode, using WFE/SEV
let executor = EXECUTOR_LOW.put(Executor::new()); let executor = EXECUTOR_LOW.put(Executor::new());