aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/executor/arch
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor/src/executor/arch')
-rw-r--r--embassy-executor/src/executor/arch/cortex_m.rs59
-rw-r--r--embassy-executor/src/executor/arch/riscv32.rs74
-rw-r--r--embassy-executor/src/executor/arch/std.rs84
-rw-r--r--embassy-executor/src/executor/arch/wasm.rs74
-rw-r--r--embassy-executor/src/executor/arch/xtensa.rs75
5 files changed, 366 insertions, 0 deletions
diff --git a/embassy-executor/src/executor/arch/cortex_m.rs b/embassy-executor/src/executor/arch/cortex_m.rs
new file mode 100644
index 000000000..d6e758dfb
--- /dev/null
+++ b/embassy-executor/src/executor/arch/cortex_m.rs
@@ -0,0 +1,59 @@
1use core::arch::asm;
2use core::marker::PhantomData;
3use core::ptr;
4
5use super::{raw, Spawner};
6
7/// Thread mode executor, using WFE/SEV.
8///
9/// This is the simplest and most common kind of executor. It runs on
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
12/// is executed, to make the `WFE` exit from sleep and poll the task.
13///
14/// This executor allows for ultra low power consumption for chips where `WFE`
15/// triggers low-power sleep without extra steps. If your chip requires extra steps,
16/// you may use [`raw::Executor`] directly to program custom behavior.
17pub struct Executor {
18 inner: raw::Executor,
19 not_send: PhantomData<*mut ()>,
20}
21
22impl Executor {
23 /// Create a new Executor.
24 pub fn new() -> Self {
25 Self {
26 inner: raw::Executor::new(|_| unsafe { asm!("sev") }, ptr::null_mut()),
27 not_send: PhantomData,
28 }
29 }
30
31 /// Run the executor.
32 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
34 /// this executor. Use it to spawn the initial task(s). After `init` returns,
35 /// the executor starts running the tasks.
36 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
38 /// for example by passing it as an argument to the initial tasks.
39 ///
40 /// This function requires `&'static mut self`. This means you have to store the
41 /// Executor instance in a place where it'll live forever and grants you mutable
42 /// access. There's a few ways to do this:
43 ///
44 /// - a [Forever](crate::util::Forever) (safe)
45 /// - a `static mut` (unsafe)
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
47 ///
48 /// This function never returns.
49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
50 init(self.inner.spawner());
51
52 loop {
53 unsafe {
54 self.inner.poll();
55 asm!("wfe");
56 };
57 }
58 }
59}
diff --git a/embassy-executor/src/executor/arch/riscv32.rs b/embassy-executor/src/executor/arch/riscv32.rs
new file mode 100644
index 000000000..7a7d5698c
--- /dev/null
+++ b/embassy-executor/src/executor/arch/riscv32.rs
@@ -0,0 +1,74 @@
1use core::marker::PhantomData;
2use core::ptr;
3
4use atomic_polyfill::{AtomicBool, Ordering};
5
6use super::{raw, Spawner};
7
8/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
9///
10static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
11
12/// RISCV32 Executor
13pub struct Executor {
14 inner: raw::Executor,
15 not_send: PhantomData<*mut ()>,
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 }
31 }
32
33 /// Run the executor.
34 ///
35 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
36 /// this executor. Use it to spawn the initial task(s). After `init` returns,
37 /// the executor starts running the tasks.
38 ///
39 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
40 /// for example by passing it as an argument to the initial tasks.
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 [Forever](crate::util::Forever) (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
54 loop {
55 unsafe {
56 self.inner.poll();
57 // we do not care about race conditions between the load and store operations, interrupts
58 //will only set this value to true.
59 critical_section::with(|_| {
60 // if there is work to do, loop back to polling
61 // TODO can we relax this?
62 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
63 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
64 }
65 // if not, wait for interrupt
66 else {
67 core::arch::asm!("wfi");
68 }
69 });
70 // if an interrupt occurred while waiting, it will be serviced here
71 }
72 }
73 }
74}
diff --git a/embassy-executor/src/executor/arch/std.rs b/embassy-executor/src/executor/arch/std.rs
new file mode 100644
index 000000000..b93ab8a79
--- /dev/null
+++ b/embassy-executor/src/executor/arch/std.rs
@@ -0,0 +1,84 @@
1use std::marker::PhantomData;
2use std::sync::{Condvar, Mutex};
3
4use super::{raw, Spawner};
5
6/// Single-threaded std-based executor.
7pub struct Executor {
8 inner: raw::Executor,
9 not_send: PhantomData<*mut ()>,
10 signaler: &'static Signaler,
11}
12
13impl Executor {
14 /// Create a new Executor.
15 pub fn new() -> Self {
16 let signaler = &*Box::leak(Box::new(Signaler::new()));
17 Self {
18 inner: raw::Executor::new(
19 |p| unsafe {
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 }
28 }
29
30 /// Run the executor.
31 ///
32 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
33 /// this executor. Use it to spawn the initial task(s). After `init` returns,
34 /// the executor starts running the tasks.
35 ///
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 [Forever](crate::util::Forever) (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
51 loop {
52 unsafe { self.inner.poll() };
53 self.signaler.wait()
54 }
55 }
56}
57
58struct Signaler {
59 mutex: Mutex<bool>,
60 condvar: Condvar,
61}
62
63impl Signaler {
64 fn new() -> Self {
65 Self {
66 mutex: Mutex::new(false),
67 condvar: Condvar::new(),
68 }
69 }
70
71 fn wait(&self) {
72 let mut signaled = self.mutex.lock().unwrap();
73 while !*signaled {
74 signaled = self.condvar.wait(signaled).unwrap();
75 }
76 *signaled = false;
77 }
78
79 fn signal(&self) {
80 let mut signaled = self.mutex.lock().unwrap();
81 *signaled = true;
82 self.condvar.notify_one();
83 }
84}
diff --git a/embassy-executor/src/executor/arch/wasm.rs b/embassy-executor/src/executor/arch/wasm.rs
new file mode 100644
index 000000000..9d5aa31ed
--- /dev/null
+++ b/embassy-executor/src/executor/arch/wasm.rs
@@ -0,0 +1,74 @@
1use core::marker::PhantomData;
2
3use js_sys::Promise;
4use wasm_bindgen::prelude::*;
5
6use super::raw::util::UninitCell;
7use super::raw::{self};
8use super::Spawner;
9
10/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
11pub struct Executor {
12 inner: raw::Executor,
13 ctx: &'static WasmContext,
14 not_send: PhantomData<*mut ()>,
15}
16
17pub(crate) struct WasmContext {
18 promise: Promise,
19 closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
20}
21
22impl WasmContext {
23 pub fn new() -> Self {
24 Self {
25 promise: Promise::resolve(&JsValue::undefined()),
26 closure: UninitCell::uninit(),
27 }
28 }
29}
30
31impl Executor {
32 /// Create a new Executor.
33 pub fn new() -> Self {
34 let ctx = &*Box::leak(Box::new(WasmContext::new()));
35 let inner = raw::Executor::new(
36 |p| unsafe {
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 }
47 }
48
49 /// Run the executor.
50 ///
51 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
52 /// this executor. Use it to spawn the initial task(s). After `init` returns,
53 /// the executor starts running the tasks.
54 ///
55 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
56 /// for example by passing it as an argument to the initial tasks.
57 ///
58 /// This function requires `&'static mut self`. This means you have to store the
59 /// Executor instance in a place where it'll live forever and grants you mutable
60 /// access. There's a few ways to do this:
61 ///
62 /// - a [Forever](crate::util::Forever) (safe)
63 /// - a `static mut` (unsafe)
64 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
65 pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
66 unsafe {
67 let executor = &self.inner;
68 self.ctx.closure.write(Closure::new(move |_| {
69 executor.poll();
70 }));
71 init(self.inner.spawner());
72 }
73 }
74}
diff --git a/embassy-executor/src/executor/arch/xtensa.rs b/embassy-executor/src/executor/arch/xtensa.rs
new file mode 100644
index 000000000..20bd7b8a5
--- /dev/null
+++ b/embassy-executor/src/executor/arch/xtensa.rs
@@ -0,0 +1,75 @@
1use core::marker::PhantomData;
2use core::ptr;
3
4use atomic_polyfill::{AtomicBool, Ordering};
5
6use super::{raw, Spawner};
7
8/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
9///
10static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
11
12/// Xtensa Executor
13pub struct Executor {
14 inner: raw::Executor,
15 not_send: PhantomData<*mut ()>,
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 }
31 }
32
33 /// Run the executor.
34 ///
35 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
36 /// this executor. Use it to spawn the initial task(s). After `init` returns,
37 /// the executor starts running the tasks.
38 ///
39 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
40 /// for example by passing it as an argument to the initial tasks.
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 [Forever](crate::util::Forever) (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
54 loop {
55 unsafe {
56 self.inner.poll();
57 // we do not care about race conditions between the load and store operations, interrupts
58 // will only set this value to true.
59 // if there is work to do, loop back to polling
60 // TODO can we relax this?
61 critical_section::with(|_| {
62 if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) {
63 SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
64 } else {
65 // waiti sets the PS.INTLEVEL when slipping into sleep
66 // because critical sections in Xtensa are implemented via increasing
67 // PS.INTLEVEL the critical section ends here
68 // 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
70 }
71 });
72 }
73 }
74 }
75}