aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch
diff options
context:
space:
mode:
authorDániel Buga <[email protected]>2023-08-12 16:00:18 +0200
committerDániel Buga <[email protected]>2023-08-12 18:29:56 +0200
commit675b7fb6056d8c3dfaca759b7cd373e2f4a0e111 (patch)
treeef4f786edd849f9ce82cffa3b1d58e939a4f53a7 /embassy-executor/src/arch
parent0727f8690c4684d0622547edee2cf9dc22215a9b (diff)
POC: allow custom executors
Diffstat (limited to 'embassy-executor/src/arch')
-rw-r--r--embassy-executor/src/arch/cortex_m.rs238
-rw-r--r--embassy-executor/src/arch/riscv32.rs100
-rw-r--r--embassy-executor/src/arch/std.rs74
-rw-r--r--embassy-executor/src/arch/wasm.rs37
-rw-r--r--embassy-executor/src/arch/xtensa.rs113
5 files changed, 190 insertions, 372 deletions
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 94c8134d6..ca1675c03 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,224 +1,98 @@
1#[cfg(feature = "executor-thread")] 1#[cfg(feature = "executor-thread")]
2pub use thread::*; 2pub use thread::*;
3
3#[cfg(feature = "executor-thread")] 4#[cfg(feature = "executor-thread")]
4mod thread { 5mod thread {
5 use core::arch::asm;
6 use core::marker::PhantomData;
7 6
8 #[cfg(feature = "nightly")] 7 #[cfg(feature = "nightly")]
9 pub use embassy_macros::main_cortex_m as main; 8 pub use embassy_macros::main_cortex_m as main;
10 9
11 use crate::raw::{Pender, PenderInner}; 10 use crate::raw::OpaqueThreadContext;
12 use crate::{raw, Spawner}; 11 use crate::thread::ThreadContext;
13
14 #[derive(Copy, Clone)]
15 pub(crate) struct ThreadPender;
16 12
17 impl ThreadPender { 13 #[export_name = "__thread_mode_pender"]
18 pub(crate) fn pend(self) { 14 fn __thread_mode_pender(_core_id: OpaqueThreadContext) {
19 unsafe { core::arch::asm!("sev") } 15 unsafe { core::arch::asm!("sev") }
20 }
21 } 16 }
22 17
23 /// Thread mode executor, using WFE/SEV. 18 /// TODO
24 /// 19 // Name pending
25 /// This is the simplest and most common kind of executor. It runs on 20 #[derive(Default)] // Default enables Executor::new
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 21 pub struct CortexMThreadContext {
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 22 _not_send: core::marker::PhantomData<*mut ()>,
28 /// is executed, to make the `WFE` exit from sleep and poll the task.
29 ///
30 /// This executor allows for ultra low power consumption for chips where `WFE`
31 /// triggers low-power sleep without extra steps. If your chip requires extra steps,
32 /// you may use [`raw::Executor`] directly to program custom behavior.
33 pub struct Executor {
34 inner: raw::Executor,
35 not_send: PhantomData<*mut ()>,
36 } 23 }
37 24
38 impl Executor { 25 impl ThreadContext for CortexMThreadContext {
39 /// Create a new Executor. 26 #[cfg(feature = "thread-context")]
40 pub fn new() -> Self { 27 fn context(&self) -> OpaqueThreadContext {
41 Self { 28 // Enabling thread-context is not incorrect, just wasteful.
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), 29 OpaqueThreadContext(0)
43 not_send: PhantomData, 30 }
44 } 31
32 #[cfg(not(feature = "thread-context"))]
33 fn context(&self) -> OpaqueThreadContext {
34 OpaqueThreadContext(())
45 } 35 }
46 36
47 /// Run the executor. 37 fn wait(&mut self) {
48 /// 38 unsafe { core::arch::asm!("wfe") }
49 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
50 /// this executor. Use it to spawn the initial task(s). After `init` returns,
51 /// the executor starts running the tasks.
52 ///
53 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
54 /// for example by passing it as an argument to the initial tasks.
55 ///
56 /// This function requires `&'static mut self`. This means you have to store the
57 /// Executor instance in a place where it'll live forever and grants you mutable
58 /// access. There's a few ways to do this:
59 ///
60 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
61 /// - a `static mut` (unsafe)
62 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
63 ///
64 /// This function never returns.
65 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
66 init(self.inner.spawner());
67
68 loop {
69 unsafe {
70 self.inner.poll();
71 asm!("wfe");
72 };
73 }
74 } 39 }
75 } 40 }
41
42 /// TODO
43 // Type alias for backwards compatibility
44 pub type Executor = crate::thread::ThreadModeExecutor<CortexMThreadContext>;
76} 45}
77 46
47// None of this has to be public, I guess?
78#[cfg(feature = "executor-interrupt")] 48#[cfg(feature = "executor-interrupt")]
79pub use interrupt::*; 49pub use interrupt::*;
80#[cfg(feature = "executor-interrupt")] 50#[cfg(feature = "executor-interrupt")]
81mod interrupt { 51mod interrupt {
82 use core::cell::UnsafeCell;
83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber; 52 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC; 53 use cortex_m::peripheral::NVIC;
88 54
89 use crate::raw::{self, Pender, PenderInner}; 55 use crate::interrupt::InterruptContext;
56 use crate::raw::OpaqueInterruptContext;
90 57
91 #[derive(Clone, Copy)] 58 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(u16); 59 struct CortexMInterruptContext(u16);
93
94 impl InterruptPender {
95 pub(crate) fn pend(self) {
96 // STIR is faster, but is only available in v7 and higher.
97 #[cfg(not(armv6m))]
98 {
99 let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
100 nvic.request(self);
101 }
102
103 #[cfg(armv6m)]
104 cortex_m::peripheral::NVIC::pend(self);
105 }
106 }
107 60
108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { 61 unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext {
109 fn number(self) -> u16 { 62 fn number(self) -> u16 {
110 self.0 63 self.0
111 } 64 }
112 } 65 }
113 66
114 /// Interrupt mode executor. 67 impl<T> InterruptContext for T
115 /// 68 where
116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up 69 T: InterruptNumber,
117 /// to poll tasks, and when a task is woken the interrupt is pended from software. 70 {
118 /// 71 fn context(&self) -> OpaqueInterruptContext {
119 /// This allows running async tasks at a priority higher than thread mode. One 72 OpaqueInterruptContext(self.number() as usize)
120 /// use case is to leave thread mode free for non-async tasks. Another use case is
121 /// to run multiple executors: one in thread mode for low priority tasks and another in
122 /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
123 /// priority ones.
124 ///
125 /// It is even possible to run multiple interrupt mode executors at different priorities,
126 /// by assigning different priorities to the interrupts. For an example on how to do this,
127 /// See the 'multiprio' example for 'embassy-nrf'.
128 ///
129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
131 /// If this is not the case, you may use an interrupt from any unused peripheral.
132 ///
133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
134 /// [`Executor`] instead, if it works for your use case.
135 pub struct InterruptExecutor {
136 started: AtomicBool,
137 executor: UnsafeCell<MaybeUninit<raw::Executor>>,
138 }
139
140 unsafe impl Send for InterruptExecutor {}
141 unsafe impl Sync for InterruptExecutor {}
142
143 impl InterruptExecutor {
144 /// Create a new, not started `InterruptExecutor`.
145 #[inline]
146 pub const fn new() -> Self {
147 Self {
148 started: AtomicBool::new(false),
149 executor: UnsafeCell::new(MaybeUninit::uninit()),
150 }
151 } 73 }
152 74
153 /// Executor interrupt callback. 75 fn enable(&self) {
154 /// 76 unsafe { NVIC::unmask(*self) }
155 /// # Safety
156 ///
157 /// You MUST call this from the interrupt handler, and from nowhere else.
158 pub unsafe fn on_interrupt(&'static self) {
159 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
160 executor.poll();
161 } 77 }
78 }
162 79
163 /// Start the executor. 80 #[export_name = "__interrupt_mode_pender"]
164 /// 81 fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) {
165 /// This initializes the executor, enables the interrupt, and returns. 82 let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 });
166 /// The executor keeps running in the background through the interrupt.
167 ///
168 /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`]
169 /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a
170 /// different "thread" (the interrupt), so spawning tasks on it is effectively
171 /// sending them.
172 ///
173 /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from
174 /// a task running in it.
175 ///
176 /// # Interrupt requirements
177 ///
178 /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).
179 ///
180 /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.
181 ///
182 /// You must set the interrupt priority before calling this method. You MUST NOT
183 /// do it after.
184 ///
185 pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner {
186 if self
187 .started
188 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
189 .is_err()
190 {
191 panic!("InterruptExecutor::start() called multiple times on the same executor.");
192 }
193
194 unsafe {
195 (&mut *self.executor.get())
196 .as_mut_ptr()
197 .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
198 irq.number(),
199 )))))
200 }
201
202 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
203
204 unsafe { NVIC::unmask(irq) }
205
206 executor.spawner().make_send()
207 }
208 83
209 /// Get a SendSpawner for this executor 84 // STIR is faster, but is only available in v7 and higher.
210 /// 85 #[cfg(not(armv6m))]
211 /// This returns a [`SendSpawner`] you can use to spawn tasks on this 86 {
212 /// executor. 87 let mut nvic: NVIC = unsafe { core::mem::transmute(()) };
213 /// 88 nvic.request(interrupt);
214 /// This MUST only be called on an executor that has already been spawned.
215 /// The function will panic otherwise.
216 pub fn spawner(&'static self) -> crate::SendSpawner {
217 if !self.started.load(Ordering::Acquire) {
218 panic!("InterruptExecutor::spawner() called on uninitialized executor.");
219 }
220 let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
221 executor.spawner().make_send()
222 } 89 }
90
91 #[cfg(armv6m)]
92 NVIC::pend(interrupt);
223 } 93 }
94
95 /// TODO
96 // Type alias for backwards compatibility
97 pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor;
224} 98}
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index ff7ec1575..5f766442d 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -1,6 +1,9 @@
1#[cfg(feature = "executor-interrupt")] 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); 2compile_error!("`executor-interrupt` is not supported with `arch-riscv32`.");
3 3
4#[cfg(feature = "thread-context")]
5compile_error!("`thread-context` is not supported with `arch-riscv32`.");
6
4#[cfg(feature = "executor-thread")] 7#[cfg(feature = "executor-thread")]
5pub use thread::*; 8pub use thread::*;
6#[cfg(feature = "executor-thread")] 9#[cfg(feature = "executor-thread")]
@@ -11,77 +14,50 @@ mod thread {
11 #[cfg(feature = "nightly")] 14 #[cfg(feature = "nightly")]
12 pub use embassy_macros::main_riscv as main; 15 pub use embassy_macros::main_riscv as main;
13 16
14 use crate::raw::{Pender, PenderInner}; 17 use crate::raw::OpaqueThreadContext;
15 use crate::{raw, Spawner}; 18 use crate::thread::ThreadContext;
16
17 #[derive(Copy, Clone)]
18 pub(crate) struct ThreadPender;
19
20 impl ThreadPender {
21 #[allow(unused)]
22 pub(crate) fn pend(self) {
23 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
24 }
25 }
26 19
27 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV 20 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
28 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); 21 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
29 22
30 /// RISCV32 Executor 23 #[export_name = "__thread_mode_pender"]
31 pub struct Executor { 24 fn __thread_mode_pender(_core_id: OpaqueThreadContext) {
32 inner: raw::Executor, 25 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
33 not_send: PhantomData<*mut ()>,
34 } 26 }
35 27
36 impl Executor { 28 /// TODO
37 /// Create a new Executor. 29 // Name pending
38 pub fn new() -> Self { 30 #[derive(Default)] // Default enables Executor::new
39 Self { 31 pub struct RiscVThreadContext {
40 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), 32 _not_send: PhantomData<*mut ()>,
41 not_send: PhantomData, 33 }
42 }
43 }
44 34
45 /// Run the executor. 35 impl ThreadContext for RiscVThreadContext {
46 /// 36 fn context(&self) -> OpaqueThreadContext {
47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 37 OpaqueThreadContext(())
48 /// this executor. Use it to spawn the initial task(s). After `init` returns, 38 }
49 /// the executor starts running the tasks.
50 ///
51 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
52 /// for example by passing it as an argument to the initial tasks.
53 ///
54 /// This function requires `&'static mut self`. This means you have to store the
55 /// Executor instance in a place where it'll live forever and grants you mutable
56 /// access. There's a few ways to do this:
57 ///
58 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
59 /// - a `static mut` (unsafe)
60 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
61 ///
62 /// This function never returns.
63 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
64 init(self.inner.spawner());
65 39
66 loop { 40 fn wait(&mut self) {
67 unsafe { 41 // We do not care about race conditions between the load and store operations,
68 self.inner.poll(); 42 // interrupts will only set this value to true.
69 // we do not care about race conditions between the load and store operations, interrupts 43 critical_section::with(|_| {
70 //will only set this value to true. 44 // if there is work to do, loop back to polling
71 critical_section::with(|_| { 45 // TODO can we relax this?
72 // if there is work to do, loop back to polling 46 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
73 // TODO can we relax this? 47 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
74 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
75 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
76 }
77 // if not, wait for interrupt
78 else {
79 core::arch::asm!("wfi");
80 }
81 });
82 // if an interrupt occurred while waiting, it will be serviced here
83 } 48 }
84 } 49 // if not, wait for interrupt
50 else {
51 unsafe {
52 core::arch::asm!("wfi");
53 }
54 }
55 });
56 // if an interrupt occurred while waiting, it will be serviced here
85 } 57 }
86 } 58 }
59
60 /// TODO
61 // Type alias for backwards compatibility
62 pub type Executor = crate::thread::ThreadModeExecutor<RiscVThreadContext>;
87} 63}
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index 4e4a178f0..28e25fbd0 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -1,6 +1,9 @@
1#[cfg(feature = "executor-interrupt")] 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-std`."); 2compile_error!("`executor-interrupt` is not supported with `arch-std`.");
3 3
4#[cfg(not(feature = "thread-context"))]
5compile_error!("`arch-std` requires `thread-context`.");
6
4#[cfg(feature = "executor-thread")] 7#[cfg(feature = "executor-thread")]
5pub use thread::*; 8pub use thread::*;
6#[cfg(feature = "executor-thread")] 9#[cfg(feature = "executor-thread")]
@@ -11,65 +14,42 @@ mod thread {
11 #[cfg(feature = "nightly")] 14 #[cfg(feature = "nightly")]
12 pub use embassy_macros::main_std as main; 15 pub use embassy_macros::main_std as main;
13 16
14 use crate::raw::{Pender, PenderInner}; 17 use crate::raw::OpaqueThreadContext;
15 use crate::{raw, Spawner}; 18 use crate::thread::ThreadContext;
16
17 #[derive(Copy, Clone)]
18 pub(crate) struct ThreadPender(&'static Signaler);
19
20 impl ThreadPender {
21 #[allow(unused)]
22 pub(crate) fn pend(self) {
23 self.0.signal()
24 }
25 }
26 19
27 /// Single-threaded std-based executor. 20 /// TODO
28 pub struct Executor { 21 // Name pending
29 inner: raw::Executor, 22 pub struct StdThreadCtx {
30 not_send: PhantomData<*mut ()>, 23 _not_send: PhantomData<*mut ()>,
31 signaler: &'static Signaler, 24 signaler: &'static Signaler,
32 } 25 }
33 26
34 impl Executor { 27 impl Default for StdThreadCtx {
35 /// Create a new Executor. 28 fn default() -> Self {
36 pub fn new() -> Self {
37 let signaler = &*Box::leak(Box::new(Signaler::new())); 29 let signaler = &*Box::leak(Box::new(Signaler::new()));
38 Self { 30 Self {
39 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), 31 _not_send: PhantomData,
40 not_send: PhantomData,
41 signaler, 32 signaler,
42 } 33 }
43 } 34 }
35 }
44 36
45 /// Run the executor. 37 impl ThreadContext for StdThreadCtx {
46 /// 38 fn context(&self) -> OpaqueThreadContext {
47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 39 OpaqueThreadContext(self.signaler as *const _ as usize)
48 /// this executor. Use it to spawn the initial task(s). After `init` returns, 40 }
49 /// the executor starts running the tasks.
50 ///
51 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
52 /// for example by passing it as an argument to the initial tasks.
53 ///
54 /// This function requires `&'static mut self`. This means you have to store the
55 /// Executor instance in a place where it'll live forever and grants you mutable
56 /// access. There's a few ways to do this:
57 ///
58 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
59 /// - a `static mut` (unsafe)
60 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
61 ///
62 /// This function never returns.
63 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
64 init(self.inner.spawner());
65 41
66 loop { 42 fn wait(&mut self) {
67 unsafe { self.inner.poll() }; 43 self.signaler.wait()
68 self.signaler.wait()
69 }
70 } 44 }
71 } 45 }
72 46
47 #[export_name = "__thread_mode_pender"]
48 fn __thread_mode_pender(core_id: OpaqueThreadContext) {
49 let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) };
50 signaler.signal()
51 }
52
73 struct Signaler { 53 struct Signaler {
74 mutex: Mutex<bool>, 54 mutex: Mutex<bool>,
75 condvar: Condvar, 55 condvar: Condvar,
@@ -97,4 +77,8 @@ mod thread {
97 self.condvar.notify_one(); 77 self.condvar.notify_one();
98 } 78 }
99 } 79 }
80
81 /// TODO
82 // Type alias for backwards compatibility
83 pub type Executor = crate::thread::ThreadModeExecutor<StdThreadCtx>;
100} 84}
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index 08ab16b99..4f5ce9c90 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -1,6 +1,9 @@
1#[cfg(feature = "executor-interrupt")] 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); 2compile_error!("`executor-interrupt` is not supported with `arch-wasm`.");
3 3
4#[cfg(not(feature = "thread-context"))]
5compile_error!("`arch-wasm` requires `thread-context`.");
6
4#[cfg(feature = "executor-thread")] 7#[cfg(feature = "executor-thread")]
5pub use thread::*; 8pub use thread::*;
6#[cfg(feature = "executor-thread")] 9#[cfg(feature = "executor-thread")]
@@ -14,14 +17,13 @@ mod thread {
14 use wasm_bindgen::prelude::*; 17 use wasm_bindgen::prelude::*;
15 18
16 use crate::raw::util::UninitCell; 19 use crate::raw::util::UninitCell;
17 use crate::raw::{Pender, PenderInner}; 20 use crate::raw::{OpaqueThreadContext, Pender, PenderInner};
18 use crate::{raw, Spawner}; 21 use crate::{raw, Spawner};
19 22
20 /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. 23 #[export_name = "__thread_mode_pender"]
21 pub struct Executor { 24 fn __thread_mode_pender(context: OpaqueThreadContext) {
22 inner: raw::Executor, 25 let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
23 ctx: &'static WasmContext, 26 let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
24 not_send: PhantomData<*mut ()>,
25 } 27 }
26 28
27 pub(crate) struct WasmContext { 29 pub(crate) struct WasmContext {
@@ -29,16 +31,6 @@ mod thread {
29 closure: UninitCell<Closure<dyn FnMut(JsValue)>>, 31 closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
30 } 32 }
31 33
32 #[derive(Copy, Clone)]
33 pub(crate) struct ThreadPender(&'static WasmContext);
34
35 impl ThreadPender {
36 #[allow(unused)]
37 pub(crate) fn pend(self) {
38 let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
39 }
40 }
41
42 impl WasmContext { 34 impl WasmContext {
43 pub fn new() -> Self { 35 pub fn new() -> Self {
44 Self { 36 Self {
@@ -48,14 +40,23 @@ mod thread {
48 } 40 }
49 } 41 }
50 42
43 /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
44 pub struct Executor {
45 inner: raw::Executor,
46 ctx: &'static WasmContext,
47 not_send: PhantomData<*mut ()>,
48 }
49
51 impl Executor { 50 impl Executor {
52 /// Create a new Executor. 51 /// Create a new Executor.
53 pub fn new() -> Self { 52 pub fn new() -> Self {
54 let ctx = &*Box::leak(Box::new(WasmContext::new())); 53 let ctx = &*Box::leak(Box::new(WasmContext::new()));
55 Self { 54 Self {
56 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), 55 inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext(
57 not_send: PhantomData, 56 ctx as *const _ as usize,
57 )))),
58 ctx, 58 ctx,
59 not_send: PhantomData,
59 } 60 }
60 } 61 }
61 62
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs
index 017b2c52b..8e1b917de 100644
--- a/embassy-executor/src/arch/xtensa.rs
+++ b/embassy-executor/src/arch/xtensa.rs
@@ -1,6 +1,12 @@
1#[cfg(feature = "executor-interrupt")] 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); 2compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
3 3
4#[cfg(feature = "thread-context")]
5compile_error!(
6 "`thread-context` is not supported with `arch-xtensa`.\
7 Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32
8);
9
4#[cfg(feature = "executor-thread")] 10#[cfg(feature = "executor-thread")]
5pub use thread::*; 11pub use thread::*;
6#[cfg(feature = "executor-thread")] 12#[cfg(feature = "executor-thread")]
@@ -8,86 +14,63 @@ mod thread {
8 use core::marker::PhantomData; 14 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering}; 15 use core::sync::atomic::{AtomicBool, Ordering};
10 16
11 use crate::raw::{Pender, PenderInner}; 17 use crate::raw::OpaqueThreadContext;
12 use crate::{raw, Spawner}; 18 use crate::thread::ThreadContext;
13 19
14 #[derive(Copy, Clone)] 20 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
15 pub(crate) struct ThreadPender; 21 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
16 22
17 impl ThreadPender { 23 #[export_name = "__thread_mode_pender"]
18 #[allow(unused)] 24 fn __thread_mode_pender(_core_id: OpaqueThreadContext) {
19 pub(crate) fn pend(self) { 25 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
21 }
22 } 26 }
23 27
24 /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa 28 /// TODO
25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); 29 // Name pending
26 30 pub struct XtensaThreadContext {
27 /// Xtensa Executor 31 _not_send: PhantomData<*mut ()>,
28 pub struct Executor {
29 inner: raw::Executor,
30 not_send: PhantomData<*mut ()>,
31 } 32 }
32 33
33 impl Executor { 34 impl Default for XtensaThreadContext {
34 /// Create a new Executor. 35 fn default() -> Self {
35 pub fn new() -> Self { 36 Self { _not_send: PhantomData }
36 Self {
37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
38 not_send: PhantomData,
39 }
40 } 37 }
38 }
41 39
42 /// Run the executor. 40 impl ThreadContext for XtensaThreadContext {
43 /// 41 fn context(&self) -> OpaqueThreadContext {
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 42 OpaqueThreadContext(())
45 /// this executor. Use it to spawn the initial task(s). After `init` returns, 43 }
46 /// the executor starts running the tasks.
47 ///
48 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
49 /// for example by passing it as an argument to the initial tasks.
50 ///
51 /// This function requires `&'static mut self`. This means you have to store the
52 /// Executor instance in a place where it'll live forever and grants you mutable
53 /// access. There's a few ways to do this:
54 ///
55 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
56 /// - a `static mut` (unsafe)
57 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
58 ///
59 /// This function never returns.
60 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
61 init(self.inner.spawner());
62
63 loop {
64 unsafe {
65 self.inner.poll();
66 44
67 // Manual critical section implementation that only masks interrupts handlers. 45 fn wait(&mut self) {
68 // We must not acquire the cross-core on dual-core systems because that would 46 unsafe {
69 // prevent the other core from doing useful work while this core is sleeping. 47 // Manual critical section implementation that only masks interrupts handlers.
70 let token: critical_section::RawRestoreState; 48 // We must not acquire the cross-core on dual-core systems because that would
71 core::arch::asm!("rsil {0}, 5", out(reg) token); 49 // prevent the other core from doing useful work while this core is sleeping.
50 let token: critical_section::RawRestoreState;
51 core::arch::asm!("rsil {0}, 5", out(reg) token);
72 52
73 // we do not care about race conditions between the load and store operations, interrupts 53 // we do not care about race conditions between the load and store operations, interrupts
74 // will only set this value to true. 54 // will only set this value to true.
75 // if there is work to do, loop back to polling 55 // if there is work to do, loop back to polling
76 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 56 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
77 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 57 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
78 58
79 core::arch::asm!( 59 core::arch::asm!(
80 "wsr.ps {0}", 60 "wsr.ps {0}",
81 "rsync", in(reg) token) 61 "rsync", in(reg) token)
82 } else { 62 } else {
83 // waiti sets the PS.INTLEVEL when slipping into sleep 63 // waiti sets the PS.INTLEVEL when slipping into sleep
84 // because critical sections in Xtensa are implemented via increasing 64 // because critical sections in Xtensa are implemented via increasing
85 // PS.INTLEVEL the critical section ends here 65 // PS.INTLEVEL the critical section ends here
86 // take care not add code after `waiti` if it needs to be inside the CS 66 // take care not add code after `waiti` if it needs to be inside the CS
87 core::arch::asm!("waiti 0"); // critical section ends here 67 core::arch::asm!("waiti 0"); // critical section ends here
88 }
89 } 68 }
90 } 69 }
91 } 70 }
92 } 71 }
72
73 /// TODO
74 // Type alias for backwards compatibility
75 pub type Executor = crate::thread::ThreadModeExecutor<XtensaThreadContext>;
93} 76}