aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch
diff options
context:
space:
mode:
authorQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
committerQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
commit6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch)
tree748f510e190bb2724750507a6e69ed1a8e08cb20 /embassy-executor/src/arch
parentd896f80405aa8963877049ed999e4aba25d6e2bb (diff)
parent6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff)
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'embassy-executor/src/arch')
-rw-r--r--embassy-executor/src/arch/cortex_m.rs253
-rw-r--r--embassy-executor/src/arch/riscv32.rs135
-rw-r--r--embassy-executor/src/arch/std.rs150
-rw-r--r--embassy-executor/src/arch/wasm.rs134
-rw-r--r--embassy-executor/src/arch/xtensa.rs126
5 files changed, 512 insertions, 286 deletions
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs
index 4b27a264e..94c8134d6 100644
--- a/embassy-executor/src/arch/cortex_m.rs
+++ b/embassy-executor/src/arch/cortex_m.rs
@@ -1,59 +1,224 @@
1use core::arch::asm; 1#[cfg(feature = "executor-thread")]
2use core::marker::PhantomData; 2pub use thread::*;
3use core::ptr; 3#[cfg(feature = "executor-thread")]
4 4mod thread {
5use super::{raw, Spawner}; 5 use core::arch::asm;
6 6 use core::marker::PhantomData;
7/// Thread mode executor, using WFE/SEV. 7
8/// 8 #[cfg(feature = "nightly")]
9/// This is the simplest and most common kind of executor. It runs on 9 pub use embassy_macros::main_cortex_m as main;
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction 10
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction 11 use crate::raw::{Pender, PenderInner};
12/// is executed, to make the `WFE` exit from sleep and poll the task. 12 use crate::{raw, Spawner};
13/// 13
14/// This executor allows for ultra low power consumption for chips where `WFE` 14 #[derive(Copy, Clone)]
15/// triggers low-power sleep without extra steps. If your chip requires extra steps, 15 pub(crate) struct ThreadPender;
16/// you may use [`raw::Executor`] directly to program custom behavior. 16
17pub struct Executor { 17 impl ThreadPender {
18 inner: raw::Executor, 18 pub(crate) fn pend(self) {
19 not_send: PhantomData<*mut ()>, 19 unsafe { core::arch::asm!("sev") }
20 }
21 }
22
23 /// Thread mode executor, using WFE/SEV.
24 ///
25 /// This is the simplest and most common kind of executor. It runs on
26 /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
27 /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
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 }
37
38 impl Executor {
39 /// Create a new Executor.
40 pub fn new() -> Self {
41 Self {
42 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
43 not_send: PhantomData,
44 }
45 }
46
47 /// Run the executor.
48 ///
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 }
75 }
20} 76}
21 77
22impl Executor { 78#[cfg(feature = "executor-interrupt")]
23 /// Create a new Executor. 79pub use interrupt::*;
24 pub fn new() -> Self { 80#[cfg(feature = "executor-interrupt")]
25 Self { 81mod interrupt {
26 inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()), 82 use core::cell::UnsafeCell;
27 not_send: PhantomData, 83 use core::mem::MaybeUninit;
84
85 use atomic_polyfill::{AtomicBool, Ordering};
86 use cortex_m::interrupt::InterruptNumber;
87 use cortex_m::peripheral::NVIC;
88
89 use crate::raw::{self, Pender, PenderInner};
90
91 #[derive(Clone, Copy)]
92 pub(crate) struct InterruptPender(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);
28 } 105 }
29 } 106 }
30 107
31 /// Run the executor. 108 unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
109 fn number(self) -> u16 {
110 self.0
111 }
112 }
113
114 /// Interrupt mode executor.
32 /// 115 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 116 /// This executor runs tasks in interrupt mode. The interrupt handler is set up
34 /// this executor. Use it to spawn the initial task(s). After `init` returns, 117 /// to poll tasks, and when a task is woken the interrupt is pended from software.
35 /// the executor starts running the tasks.
36 /// 118 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 119 /// This allows running async tasks at a priority higher than thread mode. One
38 /// for example by passing it as an argument to the initial tasks. 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.
39 /// 124 ///
40 /// This function requires `&'static mut self`. This means you have to store the 125 /// It is even possible to run multiple interrupt mode executors at different priorities,
41 /// Executor instance in a place where it'll live forever and grants you mutable 126 /// by assigning different priorities to the interrupts. For an example on how to do this,
42 /// access. There's a few ways to do this: 127 /// See the 'multiprio' example for 'embassy-nrf'.
43 /// 128 ///
44 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 129 /// To use it, you have to pick an interrupt that won't be used by the hardware.
45 /// - a `static mut` (unsafe) 130 /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 131 /// If this is not the case, you may use an interrupt from any unused peripheral.
47 /// 132 ///
48 /// This function never returns. 133 /// It is somewhat more complex to use, it's recommended to use the thread-mode
49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 134 /// [`Executor`] instead, if it works for your use case.
50 init(self.inner.spawner()); 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 }
152
153 /// Executor interrupt callback.
154 ///
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 }
162
163 /// Start the executor.
164 ///
165 /// This initializes the executor, enables the interrupt, and returns.
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 }
51 193
52 loop {
53 unsafe { 194 unsafe {
54 self.inner.poll(); 195 (&mut *self.executor.get())
55 asm!("wfe"); 196 .as_mut_ptr()
56 }; 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
209 /// Get a SendSpawner for this executor
210 ///
211 /// This returns a [`SendSpawner`] you can use to spawn tasks on this
212 /// executor.
213 ///
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()
57 } 222 }
58 } 223 }
59} 224}
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs
index 2a4b006da..ff7ec1575 100644
--- a/embassy-executor/src/arch/riscv32.rs
+++ b/embassy-executor/src/arch/riscv32.rs
@@ -1,73 +1,86 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-riscv32`.");
3 3
4use atomic_polyfill::{AtomicBool, Ordering}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
5 10
6use super::{raw, Spawner}; 11 #[cfg(feature = "nightly")]
12 pub use embassy_macros::main_riscv as main;
7 13
8/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV 14 use crate::raw::{Pender, PenderInner};
9/// 15 use crate::{raw, Spawner};
10static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
11 16
12/// RISCV32 Executor 17 #[derive(Copy, Clone)]
13pub struct Executor { 18 pub(crate) struct ThreadPender;
14 inner: raw::Executor,
15 not_send: PhantomData<*mut ()>,
16}
17 19
18impl Executor { 20 impl ThreadPender {
19 /// Create a new Executor. 21 #[allow(unused)]
20 pub fn new() -> Self { 22 pub(crate) fn pend(self) {
21 Self { 23 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
22 // use Signal_Work_Thread_Mode as substitute for local interrupt register
23 inner: raw::Executor::new(
24 |_| {
25 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
26 },
27 ptr::null_mut(),
28 ),
29 not_send: PhantomData,
30 } 24 }
31 } 25 }
32 26
33 /// Run the executor. 27 /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
34 /// 28 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
35 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 29
36 /// this executor. Use it to spawn the initial task(s). After `init` returns, 30 /// RISCV32 Executor
37 /// the executor starts running the tasks. 31 pub struct Executor {
38 /// 32 inner: raw::Executor,
39 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 33 not_send: PhantomData<*mut ()>,
40 /// for example by passing it as an argument to the initial tasks. 34 }
41 /// 35
42 /// This function requires `&'static mut self`. This means you have to store the 36 impl Executor {
43 /// Executor instance in a place where it'll live forever and grants you mutable 37 /// Create a new Executor.
44 /// access. There's a few ways to do this: 38 pub fn new() -> Self {
45 /// 39 Self {
46 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 40 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
47 /// - a `static mut` (unsafe) 41 not_send: PhantomData,
48 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 42 }
49 /// 43 }
50 /// This function never returns. 44
51 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 45 /// Run the executor.
52 init(self.inner.spawner()); 46 ///
47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
48 /// this executor. Use it to spawn the initial task(s). After `init` returns,
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());
53 65
54 loop { 66 loop {
55 unsafe { 67 unsafe {
56 self.inner.poll(); 68 self.inner.poll();
57 // we do not care about race conditions between the load and store operations, interrupts 69 // we do not care about race conditions between the load and store operations, interrupts
58 //will only set this value to true. 70 //will only set this value to true.
59 critical_section::with(|_| { 71 critical_section::with(|_| {
60 // if there is work to do, loop back to polling 72 // if there is work to do, loop back to polling
61 // TODO can we relax this? 73 // TODO can we relax this?
62 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 74 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
63 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 75 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
64 } 76 }
65 // if not, wait for interrupt 77 // if not, wait for interrupt
66 else { 78 else {
67 core::arch::asm!("wfi"); 79 core::arch::asm!("wfi");
68 } 80 }
69 }); 81 });
70 // if an interrupt occurred while waiting, it will be serviced here 82 // if an interrupt occurred while waiting, it will be serviced here
83 }
71 } 84 }
72 } 85 }
73 } 86 }
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs
index 701f0eb18..4e4a178f0 100644
--- a/embassy-executor/src/arch/std.rs
+++ b/embassy-executor/src/arch/std.rs
@@ -1,84 +1,100 @@
1use std::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use std::sync::{Condvar, Mutex}; 2compile_error!("`executor-interrupt` is not supported with `arch-std`.");
3 3
4use super::{raw, Spawner}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use std::marker::PhantomData;
9 use std::sync::{Condvar, Mutex};
5 10
6/// Single-threaded std-based executor. 11 #[cfg(feature = "nightly")]
7pub struct Executor { 12 pub use embassy_macros::main_std as main;
8 inner: raw::Executor, 13
9 not_send: PhantomData<*mut ()>, 14 use crate::raw::{Pender, PenderInner};
10 signaler: &'static Signaler, 15 use crate::{raw, Spawner};
11}
12 16
13impl Executor { 17 #[derive(Copy, Clone)]
14 /// Create a new Executor. 18 pub(crate) struct ThreadPender(&'static Signaler);
15 pub fn new() -> Self { 19
16 let signaler = &*Box::leak(Box::new(Signaler::new())); 20 impl ThreadPender {
17 Self { 21 #[allow(unused)]
18 inner: raw::Executor::new( 22 pub(crate) fn pend(self) {
19 |p| unsafe { 23 self.0.signal()
20 let s = &*(p as *const () as *const Signaler);
21 s.signal()
22 },
23 signaler as *const _ as _,
24 ),
25 not_send: PhantomData,
26 signaler,
27 } 24 }
28 } 25 }
29 26
30 /// Run the executor. 27 /// Single-threaded std-based executor.
31 /// 28 pub struct Executor {
32 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 29 inner: raw::Executor,
33 /// this executor. Use it to spawn the initial task(s). After `init` returns, 30 not_send: PhantomData<*mut ()>,
34 /// the executor starts running the tasks. 31 signaler: &'static Signaler,
35 /// 32 }
36 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
37 /// for example by passing it as an argument to the initial tasks.
38 ///
39 /// This function requires `&'static mut self`. This means you have to store the
40 /// Executor instance in a place where it'll live forever and grants you mutable
41 /// access. There's a few ways to do this:
42 ///
43 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
44 /// - a `static mut` (unsafe)
45 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
46 ///
47 /// This function never returns.
48 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
49 init(self.inner.spawner());
50 33
51 loop { 34 impl Executor {
52 unsafe { self.inner.poll() }; 35 /// Create a new Executor.
53 self.signaler.wait() 36 pub fn new() -> Self {
37 let signaler = &*Box::leak(Box::new(Signaler::new()));
38 Self {
39 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
40 not_send: PhantomData,
41 signaler,
42 }
54 } 43 }
55 }
56}
57 44
58struct Signaler { 45 /// Run the executor.
59 mutex: Mutex<bool>, 46 ///
60 condvar: Condvar, 47 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
61} 48 /// this executor. Use it to spawn the initial task(s). After `init` returns,
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());
62 65
63impl Signaler { 66 loop {
64 fn new() -> Self { 67 unsafe { self.inner.poll() };
65 Self { 68 self.signaler.wait()
66 mutex: Mutex::new(false), 69 }
67 condvar: Condvar::new(),
68 } 70 }
69 } 71 }
70 72
71 fn wait(&self) { 73 struct Signaler {
72 let mut signaled = self.mutex.lock().unwrap(); 74 mutex: Mutex<bool>,
73 while !*signaled { 75 condvar: Condvar,
74 signaled = self.condvar.wait(signaled).unwrap();
75 }
76 *signaled = false;
77 } 76 }
78 77
79 fn signal(&self) { 78 impl Signaler {
80 let mut signaled = self.mutex.lock().unwrap(); 79 fn new() -> Self {
81 *signaled = true; 80 Self {
82 self.condvar.notify_one(); 81 mutex: Mutex::new(false),
82 condvar: Condvar::new(),
83 }
84 }
85
86 fn wait(&self) {
87 let mut signaled = self.mutex.lock().unwrap();
88 while !*signaled {
89 signaled = self.condvar.wait(signaled).unwrap();
90 }
91 *signaled = false;
92 }
93
94 fn signal(&self) {
95 let mut signaled = self.mutex.lock().unwrap();
96 *signaled = true;
97 self.condvar.notify_one();
98 }
83 } 99 }
84} 100}
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs
index 98091cfbb..08ab16b99 100644
--- a/embassy-executor/src/arch/wasm.rs
+++ b/embassy-executor/src/arch/wasm.rs
@@ -1,74 +1,88 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2compile_error!("`executor-interrupt` is not supported with `arch-wasm`.");
2 3
3use js_sys::Promise; 4#[cfg(feature = "executor-thread")]
4use wasm_bindgen::prelude::*; 5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
5 8
6use super::raw::util::UninitCell; 9 use core::marker::PhantomData;
7use super::raw::{self};
8use super::Spawner;
9 10
10/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. 11 #[cfg(feature = "nightly")]
11pub struct Executor { 12 pub use embassy_macros::main_wasm as main;
12 inner: raw::Executor, 13 use js_sys::Promise;
13 ctx: &'static WasmContext, 14 use wasm_bindgen::prelude::*;
14 not_send: PhantomData<*mut ()>,
15}
16 15
17pub(crate) struct WasmContext { 16 use crate::raw::util::UninitCell;
18 promise: Promise, 17 use crate::raw::{Pender, PenderInner};
19 closure: UninitCell<Closure<dyn FnMut(JsValue)>>, 18 use crate::{raw, Spawner};
20} 19
20 /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
21 pub struct Executor {
22 inner: raw::Executor,
23 ctx: &'static WasmContext,
24 not_send: PhantomData<*mut ()>,
25 }
26
27 pub(crate) struct WasmContext {
28 promise: Promise,
29 closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
30 }
31
32 #[derive(Copy, Clone)]
33 pub(crate) struct ThreadPender(&'static WasmContext);
21 34
22impl WasmContext { 35 impl ThreadPender {
23 pub fn new() -> Self { 36 #[allow(unused)]
24 Self { 37 pub(crate) fn pend(self) {
25 promise: Promise::resolve(&JsValue::undefined()), 38 let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
26 closure: UninitCell::uninit(),
27 } 39 }
28 } 40 }
29}
30 41
31impl Executor { 42 impl WasmContext {
32 /// Create a new Executor. 43 pub fn new() -> Self {
33 pub fn new() -> Self { 44 Self {
34 let ctx = &*Box::leak(Box::new(WasmContext::new())); 45 promise: Promise::resolve(&JsValue::undefined()),
35 let inner = raw::Executor::new( 46 closure: UninitCell::uninit(),
36 |p| unsafe { 47 }
37 let ctx = &*(p as *const () as *const WasmContext);
38 let _ = ctx.promise.then(ctx.closure.as_mut());
39 },
40 ctx as *const _ as _,
41 );
42 Self {
43 inner,
44 not_send: PhantomData,
45 ctx,
46 } 48 }
47 } 49 }
48 50
49 /// Run the executor. 51 impl Executor {
50 /// 52 /// Create a new Executor.
51 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 53 pub fn new() -> Self {
52 /// this executor. Use it to spawn the initial task(s). After `init` returns, 54 let ctx = &*Box::leak(Box::new(WasmContext::new()));
53 /// the executor starts running the tasks. 55 Self {
54 /// 56 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
55 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 57 not_send: PhantomData,
56 /// for example by passing it as an argument to the initial tasks. 58 ctx,
57 /// 59 }
58 /// This function requires `&'static mut self`. This means you have to store the 60 }
59 /// Executor instance in a place where it'll live forever and grants you mutable 61
60 /// access. There's a few ways to do this: 62 /// Run the executor.
61 /// 63 ///
62 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) 64 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
63 /// - a `static mut` (unsafe) 65 /// this executor. Use it to spawn the initial task(s). After `init` returns,
64 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) 66 /// the executor starts running the tasks.
65 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { 67 ///
66 unsafe { 68 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
67 let executor = &self.inner; 69 /// for example by passing it as an argument to the initial tasks.
68 self.ctx.closure.write(Closure::new(move |_| { 70 ///
69 executor.poll(); 71 /// This function requires `&'static mut self`. This means you have to store the
70 })); 72 /// Executor instance in a place where it'll live forever and grants you mutable
71 init(self.inner.spawner()); 73 /// access. There's a few ways to do this:
74 ///
75 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
76 /// - a `static mut` (unsafe)
77 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
78 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
79 unsafe {
80 let executor = &self.inner;
81 self.ctx.closure.write(Closure::new(move |_| {
82 executor.poll();
83 }));
84 init(self.inner.spawner());
85 }
72 } 86 }
73 } 87 }
74} 88}
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs
index f908aaa70..017b2c52b 100644
--- a/embassy-executor/src/arch/xtensa.rs
+++ b/embassy-executor/src/arch/xtensa.rs
@@ -1,66 +1,84 @@
1use core::marker::PhantomData; 1#[cfg(feature = "executor-interrupt")]
2use core::ptr; 2compile_error!("`executor-interrupt` is not supported with `arch-xtensa`.");
3 3
4use atomic_polyfill::{AtomicBool, Ordering}; 4#[cfg(feature = "executor-thread")]
5pub use thread::*;
6#[cfg(feature = "executor-thread")]
7mod thread {
8 use core::marker::PhantomData;
9 use core::sync::atomic::{AtomicBool, Ordering};
5 10
6use super::{raw, Spawner}; 11 use crate::raw::{Pender, PenderInner};
12 use crate::{raw, Spawner};
7 13
8/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa 14 #[derive(Copy, Clone)]
9/// 15 pub(crate) struct ThreadPender;
10static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
11 16
12/// Xtensa Executor 17 impl ThreadPender {
13pub struct Executor { 18 #[allow(unused)]
14 inner: raw::Executor, 19 pub(crate) fn pend(self) {
15 not_send: PhantomData<*mut ()>, 20 SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
16}
17
18impl Executor {
19 /// Create a new Executor.
20 pub fn new() -> Self {
21 Self {
22 // use Signal_Work_Thread_Mode as substitute for local interrupt register
23 inner: raw::Executor::new(
24 |_| {
25 SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
26 },
27 ptr::null_mut(),
28 ),
29 not_send: PhantomData,
30 } 21 }
31 } 22 }
32 23
33 /// Run the executor. 24 /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
34 /// 25 static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
35 /// The `init` closure is called with a [`Spawner`] that spawns tasks on 26
36 /// this executor. Use it to spawn the initial task(s). After `init` returns, 27 /// Xtensa Executor
37 /// the executor starts running the tasks. 28 pub struct Executor {
38 /// 29 inner: raw::Executor,
39 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), 30 not_send: PhantomData<*mut ()>,
40 /// for example by passing it as an argument to the initial tasks. 31 }
41 ///
42 /// This function requires `&'static mut self`. This means you have to store the
43 /// Executor instance in a place where it'll live forever and grants you mutable
44 /// access. There's a few ways to do this:
45 ///
46 /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe)
47 /// - a `static mut` (unsafe)
48 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
49 ///
50 /// This function never returns.
51 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
52 init(self.inner.spawner());
53 32
54 loop { 33 impl Executor {
55 unsafe { 34 /// Create a new Executor.
56 self.inner.poll(); 35 pub fn new() -> Self {
57 // we do not care about race conditions between the load and store operations, interrupts 36 Self {
58 // will only set this value to true. 37 inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
59 // if there is work to do, loop back to polling 38 not_send: PhantomData,
60 // TODO can we relax this? 39 }
61 critical_section::with(|_| { 40 }
41
42 /// Run the executor.
43 ///
44 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
45 /// this executor. Use it to spawn the initial task(s). After `init` returns,
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
67 // Manual critical section implementation that only masks interrupts handlers.
68 // We must not acquire the cross-core on dual-core systems because that would
69 // prevent the other core from doing useful work while this core is sleeping.
70 let token: critical_section::RawRestoreState;
71 core::arch::asm!("rsil {0}, 5", out(reg) token);
72
73 // we do not care about race conditions between the load and store operations, interrupts
74 // will only set this value to true.
75 // if there is work to do, loop back to polling
62 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { 76 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
63 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); 77 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
78
79 core::arch::asm!(
80 "wsr.ps {0}",
81 "rsync", in(reg) token)
64 } else { 82 } else {
65 // waiti sets the PS.INTLEVEL when slipping into sleep 83 // waiti sets the PS.INTLEVEL when slipping into sleep
66 // because critical sections in Xtensa are implemented via increasing 84 // because critical sections in Xtensa are implemented via increasing
@@ -68,7 +86,7 @@ impl Executor {
68 // take care not add code after `waiti` if it needs to be inside the CS 86 // take care not add code after `waiti` if it needs to be inside the CS
69 core::arch::asm!("waiti 0"); // critical section ends here 87 core::arch::asm!("waiti 0"); // critical section ends here
70 } 88 }
71 }); 89 }
72 } 90 }
73 } 91 }
74 } 92 }