diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-11-24 21:37:27 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-11-24 23:52:09 +0100 |
| commit | 171cdb94c7906670723b0965ca66d72a2352ac73 (patch) | |
| tree | 92481f40c2ea3de70055e5b967629b5c93c4b298 | |
| parent | 1fbc150fd6392d8268aa35d15380c02e363c4eb8 (diff) | |
executor: add support for main/task macros in stable (allocates tasks in an arena)
| -rwxr-xr-x | .github/ci/test.sh | 1 | ||||
| -rw-r--r-- | embassy-executor/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-executor/src/arch/cortex_m.rs | 1 | ||||
| -rw-r--r-- | embassy-executor/src/arch/riscv32.rs | 1 | ||||
| -rw-r--r-- | embassy-executor/src/arch/std.rs | 1 | ||||
| -rw-r--r-- | embassy-executor/src/arch/wasm.rs | 1 | ||||
| -rw-r--r-- | embassy-executor/src/arch/xtensa.rs | 1 | ||||
| -rw-r--r-- | embassy-executor/src/lib.rs | 95 | ||||
| -rw-r--r-- | embassy-macros/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-macros/src/macros/task.rs | 9 |
10 files changed, 105 insertions, 10 deletions
diff --git a/.github/ci/test.sh b/.github/ci/test.sh index e48d6e65b..dfc2b08ce 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh | |||
| @@ -15,6 +15,7 @@ export CARGO_NET_GIT_FETCH_WITH_CLI=true | |||
| 15 | hashtime restore /ci/cache/filetime.json || true | 15 | hashtime restore /ci/cache/filetime.json || true |
| 16 | hashtime save /ci/cache/filetime.json | 16 | hashtime save /ci/cache/filetime.json |
| 17 | 17 | ||
| 18 | MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml | ||
| 18 | MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly | 19 | MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly |
| 19 | 20 | ||
| 20 | cargo test --manifest-path ./embassy-sync/Cargo.toml | 21 | cargo test --manifest-path ./embassy-sync/Cargo.toml |
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index f0e85a2c8..3ff8440ca 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -43,7 +43,7 @@ executor-thread = [] | |||
| 43 | executor-interrupt = [] | 43 | executor-interrupt = [] |
| 44 | 44 | ||
| 45 | # Enable nightly-only features | 45 | # Enable nightly-only features |
| 46 | nightly = [] | 46 | nightly = ["embassy-macros/nightly"] |
| 47 | 47 | ||
| 48 | turbowakers = [] | 48 | turbowakers = [] |
| 49 | 49 | ||
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 55299c94f..4a6d58575 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs | |||
| @@ -51,7 +51,6 @@ mod thread { | |||
| 51 | use core::arch::asm; | 51 | use core::arch::asm; |
| 52 | use core::marker::PhantomData; | 52 | use core::marker::PhantomData; |
| 53 | 53 | ||
| 54 | #[cfg(feature = "nightly")] | ||
| 55 | pub use embassy_macros::main_cortex_m as main; | 54 | pub use embassy_macros::main_cortex_m as main; |
| 56 | 55 | ||
| 57 | use crate::{raw, Spawner}; | 56 | use crate::{raw, Spawner}; |
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 6814e7844..ca12c3403 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs | |||
| @@ -7,7 +7,6 @@ pub use thread::*; | |||
| 7 | mod thread { | 7 | mod thread { |
| 8 | use core::marker::PhantomData; | 8 | use core::marker::PhantomData; |
| 9 | 9 | ||
| 10 | #[cfg(feature = "nightly")] | ||
| 11 | pub use embassy_macros::main_riscv as main; | 10 | pub use embassy_macros::main_riscv as main; |
| 12 | use portable_atomic::{AtomicBool, Ordering}; | 11 | use portable_atomic::{AtomicBool, Ordering}; |
| 13 | 12 | ||
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 5b2f7e2e4..598bb0509 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs | |||
| @@ -8,7 +8,6 @@ mod thread { | |||
| 8 | use std::marker::PhantomData; | 8 | use std::marker::PhantomData; |
| 9 | use std::sync::{Condvar, Mutex}; | 9 | use std::sync::{Condvar, Mutex}; |
| 10 | 10 | ||
| 11 | #[cfg(feature = "nightly")] | ||
| 12 | pub use embassy_macros::main_std as main; | 11 | pub use embassy_macros::main_std as main; |
| 13 | 12 | ||
| 14 | use crate::{raw, Spawner}; | 13 | use crate::{raw, Spawner}; |
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 15aed867a..3faa92575 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs | |||
| @@ -8,7 +8,6 @@ mod thread { | |||
| 8 | 8 | ||
| 9 | use core::marker::PhantomData; | 9 | use core::marker::PhantomData; |
| 10 | 10 | ||
| 11 | #[cfg(feature = "nightly")] | ||
| 12 | pub use embassy_macros::main_wasm as main; | 11 | pub use embassy_macros::main_wasm as main; |
| 13 | use js_sys::Promise; | 12 | use js_sys::Promise; |
| 14 | use wasm_bindgen::prelude::*; | 13 | use wasm_bindgen::prelude::*; |
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index d335594e2..6ed9f9e72 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs | |||
| @@ -8,7 +8,6 @@ mod thread { | |||
| 8 | use core::marker::PhantomData; | 8 | use core::marker::PhantomData; |
| 9 | use core::sync::atomic::{AtomicBool, Ordering}; | 9 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 10 | 10 | ||
| 11 | #[cfg(feature = "nightly")] | ||
| 12 | pub use embassy_macros::main_riscv as main; | 11 | pub use embassy_macros::main_riscv as main; |
| 13 | 12 | ||
| 14 | use crate::{raw, Spawner}; | 13 | use crate::{raw, Spawner}; |
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3b61b4ba5..ac7dbb035 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] | 1 | #![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] |
| 2 | #![cfg_attr(all(feature = "nightly", feature = "arch-xtensa"), feature(asm_experimental_arch))] | 2 | #![cfg_attr(feature = "arch-xtensa", feature(asm_experimental_arch))] |
| 3 | #![allow(clippy::new_without_default)] | 3 | #![allow(clippy::new_without_default)] |
| 4 | #![doc = include_str!("../README.md")] | 4 | #![doc = include_str!("../README.md")] |
| 5 | #![warn(missing_docs)] | 5 | #![warn(missing_docs)] |
| @@ -7,7 +7,6 @@ | |||
| 7 | // This mod MUST go first, so that the others see its macros. | 7 | // This mod MUST go first, so that the others see its macros. |
| 8 | pub(crate) mod fmt; | 8 | pub(crate) mod fmt; |
| 9 | 9 | ||
| 10 | #[cfg(feature = "nightly")] | ||
| 11 | pub use embassy_macros::task; | 10 | pub use embassy_macros::task; |
| 12 | 11 | ||
| 13 | macro_rules! check_at_most_one { | 12 | macro_rules! check_at_most_one { |
| @@ -44,4 +43,94 @@ pub use spawner::*; | |||
| 44 | /// Implementation details for embassy macros. | 43 | /// Implementation details for embassy macros. |
| 45 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | 44 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. |
| 46 | #[doc(hidden)] | 45 | #[doc(hidden)] |
| 47 | pub mod _export {} | 46 | #[cfg(not(feature = "nightly"))] |
| 47 | pub mod _export { | ||
| 48 | use core::alloc::Layout; | ||
| 49 | use core::cell::{Cell, UnsafeCell}; | ||
| 50 | use core::future::Future; | ||
| 51 | use core::mem::MaybeUninit; | ||
| 52 | use core::ptr::null_mut; | ||
| 53 | |||
| 54 | use critical_section::{CriticalSection, Mutex}; | ||
| 55 | |||
| 56 | use crate::raw::TaskPool; | ||
| 57 | |||
| 58 | struct Arena<const N: usize> { | ||
| 59 | buf: UnsafeCell<MaybeUninit<[u8; N]>>, | ||
| 60 | ptr: Mutex<Cell<*mut u8>>, | ||
| 61 | } | ||
| 62 | |||
| 63 | unsafe impl<const N: usize> Sync for Arena<N> {} | ||
| 64 | unsafe impl<const N: usize> Send for Arena<N> {} | ||
| 65 | |||
| 66 | impl<const N: usize> Arena<N> { | ||
| 67 | const fn new() -> Self { | ||
| 68 | Self { | ||
| 69 | buf: UnsafeCell::new(MaybeUninit::uninit()), | ||
| 70 | ptr: Mutex::new(Cell::new(null_mut())), | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> { | ||
| 75 | let layout = Layout::new::<T>(); | ||
| 76 | |||
| 77 | let start = self.buf.get().cast::<u8>(); | ||
| 78 | let end = unsafe { start.add(N) }; | ||
| 79 | |||
| 80 | let mut ptr = self.ptr.borrow(cs).get(); | ||
| 81 | if ptr.is_null() { | ||
| 82 | ptr = self.buf.get().cast::<u8>(); | ||
| 83 | } | ||
| 84 | |||
| 85 | let bytes_left = (end as usize) - (ptr as usize); | ||
| 86 | let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize); | ||
| 87 | |||
| 88 | if align_offset + layout.size() > bytes_left { | ||
| 89 | panic!("arena full"); | ||
| 90 | } | ||
| 91 | |||
| 92 | let res = unsafe { ptr.add(align_offset) }; | ||
| 93 | let ptr = unsafe { ptr.add(align_offset + layout.size()) }; | ||
| 94 | |||
| 95 | self.ptr.borrow(cs).set(ptr); | ||
| 96 | |||
| 97 | unsafe { &mut *(res as *mut MaybeUninit<T>) } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | const ARENA_SIZE: usize = 16 * 1024; | ||
| 102 | static ARENA: Arena<ARENA_SIZE> = Arena::new(); | ||
| 103 | |||
| 104 | pub struct TaskPoolRef { | ||
| 105 | // type-erased `&'static mut TaskPool<F, N>` | ||
| 106 | // Needed because statics can't have generics. | ||
| 107 | ptr: Mutex<Cell<*mut ()>>, | ||
| 108 | } | ||
| 109 | unsafe impl Sync for TaskPoolRef {} | ||
| 110 | unsafe impl Send for TaskPoolRef {} | ||
| 111 | |||
| 112 | impl TaskPoolRef { | ||
| 113 | pub const fn new() -> Self { | ||
| 114 | Self { | ||
| 115 | ptr: Mutex::new(Cell::new(null_mut())), | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Get the pool for this ref, allocating it from the arena the first time. | ||
| 120 | /// | ||
| 121 | /// safety: for a given TaskPoolRef instance, must always call with the exact | ||
| 122 | /// same generic params. | ||
| 123 | pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> { | ||
| 124 | critical_section::with(|cs| { | ||
| 125 | let ptr = self.ptr.borrow(cs); | ||
| 126 | if ptr.get().is_null() { | ||
| 127 | let pool = ARENA.alloc::<TaskPool<F, N>>(cs); | ||
| 128 | pool.write(TaskPool::new()); | ||
| 129 | ptr.set(pool as *mut _ as _); | ||
| 130 | } | ||
| 131 | |||
| 132 | unsafe { &*(ptr.get() as *const _) } | ||
| 133 | }) | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/embassy-macros/Cargo.toml b/embassy-macros/Cargo.toml index 4e94bf834..e13104cfd 100644 --- a/embassy-macros/Cargo.toml +++ b/embassy-macros/Cargo.toml | |||
| @@ -20,4 +20,5 @@ proc-macro2 = "1.0.29" | |||
| 20 | [lib] | 20 | [lib] |
| 21 | proc-macro = true | 21 | proc-macro = true |
| 22 | 22 | ||
| 23 | [features] \ No newline at end of file | 23 | [features] |
| 24 | nightly = [] \ No newline at end of file | ||
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs index 1d30434e9..5161e1020 100644 --- a/embassy-macros/src/macros/task.rs +++ b/embassy-macros/src/macros/task.rs | |||
| @@ -79,6 +79,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre | |||
| 79 | task_inner.vis = syn::Visibility::Inherited; | 79 | task_inner.vis = syn::Visibility::Inherited; |
| 80 | task_inner.sig.ident = task_inner_ident.clone(); | 80 | task_inner.sig.ident = task_inner_ident.clone(); |
| 81 | 81 | ||
| 82 | #[cfg(feature = "nightly")] | ||
| 82 | let mut task_outer: ItemFn = parse_quote! { | 83 | let mut task_outer: ItemFn = parse_quote! { |
| 83 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | 84 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { |
| 84 | type Fut = impl ::core::future::Future + 'static; | 85 | type Fut = impl ::core::future::Future + 'static; |
| @@ -87,6 +88,14 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStre | |||
| 87 | unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | 88 | unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } |
| 88 | } | 89 | } |
| 89 | }; | 90 | }; |
| 91 | #[cfg(not(feature = "nightly"))] | ||
| 92 | let mut task_outer: ItemFn = parse_quote! { | ||
| 93 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | ||
| 94 | const POOL_SIZE: usize = #pool_size; | ||
| 95 | static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); | ||
| 96 | unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | ||
| 97 | } | ||
| 98 | }; | ||
| 90 | 99 | ||
| 91 | task_outer.attrs.append(&mut task_inner.attrs.clone()); | 100 | task_outer.attrs.append(&mut task_inner.attrs.clone()); |
| 92 | 101 | ||
