aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/arch/wasm.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-04-03 01:18:27 +0200
committerDario Nieuwenhuis <[email protected]>2023-04-03 03:09:11 +0200
commitd3c4e4a20a05085eae8d568c7efdbe09bada9cf5 (patch)
treeef92420c5a4574c9db5cb614d0ecc49d6462badc /embassy-executor/src/arch/wasm.rs
parentb41ee47115509ba5ed302c684c0c36b6a3e3f76f (diff)
executor: add Pender, rework Cargo features.
This introduces a `Pender` struct with enum cases for thread-mode, interrupt-mode and custom callback executors. This avoids calls through function pointers when using only the thread or interrupt executors. Faster, and friendlier to `cargo-call-stack`. `embassy-executor` now has `arch-xxx` Cargo features to select the arch and to enable the builtin executors (thread and interrupt).
Diffstat (limited to 'embassy-executor/src/arch/wasm.rs')
-rw-r--r--embassy-executor/src/arch/wasm.rs134
1 files changed, 74 insertions, 60 deletions
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}