diff options
| author | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
|---|---|---|
| committer | Dion Dokter <[email protected]> | 2025-11-20 13:22:38 +0100 |
| commit | 4f2c36e447455e8d33607d586859d3d075cabf1d (patch) | |
| tree | 003cd822d688acd7c074dd229663b4648d100f71 /embassy-executor/src/lib.rs | |
| parent | 663732d85abbae400f2dbab2c411802a5b60e9b1 (diff) | |
| parent | 661874d11de7d93ed52e08e020a9d4c7ee11122d (diff) | |
Merge branch 'main' into u0-lcd
Diffstat (limited to 'embassy-executor/src/lib.rs')
| -rw-r--r-- | embassy-executor/src/lib.rs | 254 |
1 files changed, 185 insertions, 69 deletions
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 553ed76d3..cffc76699 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -1,6 +1,6 @@ | |||
| 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(feature = "nightly", feature(waker_getters))] | ||
| 3 | #![allow(clippy::new_without_default)] | 2 | #![allow(clippy::new_without_default)] |
| 3 | #![allow(unsafe_op_in_unsafe_fn)] | ||
| 4 | #![doc = include_str!("../README.md")] | 4 | #![doc = include_str!("../README.md")] |
| 5 | #![warn(missing_docs)] | 5 | #![warn(missing_docs)] |
| 6 | 6 | ||
| @@ -24,120 +24,236 @@ macro_rules! check_at_most_one { | |||
| 24 | check_at_most_one!(@amo [$($f)*] [$($f)*] []); | 24 | check_at_most_one!(@amo [$($f)*] [$($f)*] []); |
| 25 | }; | 25 | }; |
| 26 | } | 26 | } |
| 27 | check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",); | 27 | check_at_most_one!( |
| 28 | "arch-avr", | ||
| 29 | "arch-cortex-m", | ||
| 30 | "arch-cortex-ar", | ||
| 31 | "arch-riscv32", | ||
| 32 | "arch-std", | ||
| 33 | "arch-wasm", | ||
| 34 | "arch-spin", | ||
| 35 | ); | ||
| 28 | 36 | ||
| 29 | #[cfg(feature = "_arch")] | 37 | #[cfg(feature = "_arch")] |
| 30 | #[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")] | 38 | #[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")] |
| 31 | #[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")] | 39 | #[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")] |
| 40 | #[cfg_attr(feature = "arch-cortex-ar", path = "arch/cortex_ar.rs")] | ||
| 32 | #[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] | 41 | #[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] |
| 33 | #[cfg_attr(feature = "arch-std", path = "arch/std.rs")] | 42 | #[cfg_attr(feature = "arch-std", path = "arch/std.rs")] |
| 34 | #[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] | 43 | #[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] |
| 44 | #[cfg_attr(feature = "arch-spin", path = "arch/spin.rs")] | ||
| 35 | mod arch; | 45 | mod arch; |
| 36 | 46 | ||
| 37 | #[cfg(feature = "_arch")] | 47 | #[cfg(feature = "_arch")] |
| 38 | #[allow(unused_imports)] // don't warn if the module is empty. | 48 | #[allow(unused_imports)] // don't warn if the module is empty. |
| 39 | pub use arch::*; | 49 | pub use arch::*; |
| 50 | #[cfg(not(feature = "_arch"))] | ||
| 51 | pub use embassy_executor_macros::main_unspecified as main; | ||
| 40 | 52 | ||
| 41 | pub mod raw; | 53 | pub mod raw; |
| 42 | 54 | ||
| 43 | mod spawner; | 55 | mod spawner; |
| 44 | pub use spawner::*; | 56 | pub use spawner::*; |
| 45 | 57 | ||
| 46 | mod config { | 58 | mod metadata; |
| 47 | #![allow(unused)] | 59 | pub use metadata::*; |
| 48 | include!(concat!(env!("OUT_DIR"), "/config.rs")); | ||
| 49 | } | ||
| 50 | 60 | ||
| 51 | /// Implementation details for embassy macros. | 61 | /// Implementation details for embassy macros. |
| 52 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | 62 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. |
| 53 | #[doc(hidden)] | 63 | #[doc(hidden)] |
| 54 | #[cfg(not(feature = "nightly"))] | 64 | #[cfg(not(feature = "nightly"))] |
| 55 | pub mod _export { | 65 | pub mod _export { |
| 56 | use core::alloc::Layout; | 66 | use core::cell::UnsafeCell; |
| 57 | use core::cell::{Cell, UnsafeCell}; | ||
| 58 | use core::future::Future; | 67 | use core::future::Future; |
| 59 | use core::mem::MaybeUninit; | 68 | use core::mem::MaybeUninit; |
| 60 | use core::ptr::null_mut; | ||
| 61 | |||
| 62 | use critical_section::{CriticalSection, Mutex}; | ||
| 63 | 69 | ||
| 64 | use crate::raw::TaskPool; | 70 | use crate::raw::TaskPool; |
| 65 | 71 | ||
| 66 | struct Arena<const N: usize> { | 72 | trait TaskReturnValue {} |
| 67 | buf: UnsafeCell<MaybeUninit<[u8; N]>>, | 73 | impl TaskReturnValue for () {} |
| 68 | ptr: Mutex<Cell<*mut u8>>, | 74 | impl TaskReturnValue for Never {} |
| 69 | } | ||
| 70 | 75 | ||
| 71 | unsafe impl<const N: usize> Sync for Arena<N> {} | 76 | #[diagnostic::on_unimplemented( |
| 72 | unsafe impl<const N: usize> Send for Arena<N> {} | 77 | message = "task futures must resolve to `()` or `!`", |
| 78 | note = "use `async fn` or change the return type to `impl Future<Output = ()>`" | ||
| 79 | )] | ||
| 80 | #[allow(private_bounds)] | ||
| 81 | pub trait TaskFn<Args>: Copy { | ||
| 82 | type Fut: Future<Output: TaskReturnValue> + 'static; | ||
| 83 | } | ||
| 73 | 84 | ||
| 74 | impl<const N: usize> Arena<N> { | 85 | macro_rules! task_fn_impl { |
| 75 | const fn new() -> Self { | 86 | ($($Tn:ident),*) => { |
| 76 | Self { | 87 | impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F |
| 77 | buf: UnsafeCell::new(MaybeUninit::uninit()), | 88 | where |
| 78 | ptr: Mutex::new(Cell::new(null_mut())), | 89 | F: Copy + FnOnce($($Tn,)*) -> Fut, |
| 90 | Fut: Future<Output: TaskReturnValue> + 'static, | ||
| 91 | { | ||
| 92 | type Fut = Fut; | ||
| 79 | } | 93 | } |
| 80 | } | 94 | }; |
| 95 | } | ||
| 81 | 96 | ||
| 82 | fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> { | 97 | task_fn_impl!(); |
| 83 | let layout = Layout::new::<T>(); | 98 | task_fn_impl!(T0); |
| 99 | task_fn_impl!(T0, T1); | ||
| 100 | task_fn_impl!(T0, T1, T2); | ||
| 101 | task_fn_impl!(T0, T1, T2, T3); | ||
| 102 | task_fn_impl!(T0, T1, T2, T3, T4); | ||
| 103 | task_fn_impl!(T0, T1, T2, T3, T4, T5); | ||
| 104 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6); | ||
| 105 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7); | ||
| 106 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8); | ||
| 107 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9); | ||
| 108 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); | ||
| 109 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); | ||
| 110 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); | ||
| 111 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); | ||
| 112 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); | ||
| 113 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); | ||
| 84 | 114 | ||
| 85 | let start = self.buf.get().cast::<u8>(); | 115 | #[allow(private_bounds)] |
| 86 | let end = unsafe { start.add(N) }; | 116 | #[repr(C)] |
| 117 | pub struct TaskPoolHolder<const SIZE: usize, const ALIGN: usize> | ||
| 118 | where | ||
| 119 | Align<ALIGN>: Alignment, | ||
| 120 | { | ||
| 121 | data: UnsafeCell<[MaybeUninit<u8>; SIZE]>, | ||
| 122 | align: Align<ALIGN>, | ||
| 123 | } | ||
| 87 | 124 | ||
| 88 | let mut ptr = self.ptr.borrow(cs).get(); | 125 | unsafe impl<const SIZE: usize, const ALIGN: usize> Send for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {} |
| 89 | if ptr.is_null() { | 126 | unsafe impl<const SIZE: usize, const ALIGN: usize> Sync for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {} |
| 90 | ptr = self.buf.get().cast::<u8>(); | ||
| 91 | } | ||
| 92 | 127 | ||
| 93 | let bytes_left = (end as usize) - (ptr as usize); | 128 | #[allow(private_bounds)] |
| 94 | let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize); | 129 | impl<const SIZE: usize, const ALIGN: usize> TaskPoolHolder<SIZE, ALIGN> |
| 130 | where | ||
| 131 | Align<ALIGN>: Alignment, | ||
| 132 | { | ||
| 133 | pub const fn get(&self) -> *const u8 { | ||
| 134 | self.data.get().cast() | ||
| 135 | } | ||
| 136 | } | ||
| 95 | 137 | ||
| 96 | if align_offset + layout.size() > bytes_left { | 138 | pub const fn task_pool_size<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize |
| 97 | panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/"); | 139 | where |
| 98 | } | 140 | F: TaskFn<Args, Fut = Fut>, |
| 141 | Fut: Future + 'static, | ||
| 142 | { | ||
| 143 | size_of::<TaskPool<Fut, POOL_SIZE>>() | ||
| 144 | } | ||
| 145 | |||
| 146 | pub const fn task_pool_align<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> usize | ||
| 147 | where | ||
| 148 | F: TaskFn<Args, Fut = Fut>, | ||
| 149 | Fut: Future + 'static, | ||
| 150 | { | ||
| 151 | align_of::<TaskPool<Fut, POOL_SIZE>>() | ||
| 152 | } | ||
| 99 | 153 | ||
| 100 | let res = unsafe { ptr.add(align_offset) }; | 154 | pub const fn task_pool_new<F, Args, Fut, const POOL_SIZE: usize>(_: F) -> TaskPool<Fut, POOL_SIZE> |
| 101 | let ptr = unsafe { ptr.add(align_offset + layout.size()) }; | 155 | where |
| 156 | F: TaskFn<Args, Fut = Fut>, | ||
| 157 | Fut: Future + 'static, | ||
| 158 | { | ||
| 159 | TaskPool::new() | ||
| 160 | } | ||
| 102 | 161 | ||
| 103 | self.ptr.borrow(cs).set(ptr); | 162 | #[allow(private_bounds)] |
| 163 | #[repr(transparent)] | ||
| 164 | pub struct Align<const N: usize>([<Self as Alignment>::Archetype; 0]) | ||
| 165 | where | ||
| 166 | Self: Alignment; | ||
| 104 | 167 | ||
| 105 | unsafe { &mut *(res as *mut MaybeUninit<T>) } | 168 | trait Alignment { |
| 106 | } | 169 | /// A zero-sized type of particular alignment. |
| 170 | type Archetype: Copy + Eq + PartialEq + Send + Sync + Unpin; | ||
| 107 | } | 171 | } |
| 108 | 172 | ||
| 109 | static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new(); | 173 | macro_rules! aligns { |
| 174 | ($($AlignX:ident: $n:literal,)*) => { | ||
| 175 | $( | ||
| 176 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 177 | #[repr(align($n))] | ||
| 178 | struct $AlignX {} | ||
| 179 | impl Alignment for Align<$n> { | ||
| 180 | type Archetype = $AlignX; | ||
| 181 | } | ||
| 182 | )* | ||
| 183 | }; | ||
| 184 | } | ||
| 185 | |||
| 186 | aligns!( | ||
| 187 | Align1: 1, | ||
| 188 | Align2: 2, | ||
| 189 | Align4: 4, | ||
| 190 | Align8: 8, | ||
| 191 | Align16: 16, | ||
| 192 | Align32: 32, | ||
| 193 | Align64: 64, | ||
| 194 | Align128: 128, | ||
| 195 | Align256: 256, | ||
| 196 | Align512: 512, | ||
| 197 | Align1024: 1024, | ||
| 198 | Align2048: 2048, | ||
| 199 | Align4096: 4096, | ||
| 200 | Align8192: 8192, | ||
| 201 | Align16384: 16384, | ||
| 202 | ); | ||
| 203 | #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] | ||
| 204 | aligns!( | ||
| 205 | Align32768: 32768, | ||
| 206 | Align65536: 65536, | ||
| 207 | Align131072: 131072, | ||
| 208 | Align262144: 262144, | ||
| 209 | Align524288: 524288, | ||
| 210 | Align1048576: 1048576, | ||
| 211 | Align2097152: 2097152, | ||
| 212 | Align4194304: 4194304, | ||
| 213 | Align8388608: 8388608, | ||
| 214 | Align16777216: 16777216, | ||
| 215 | Align33554432: 33554432, | ||
| 216 | Align67108864: 67108864, | ||
| 217 | Align134217728: 134217728, | ||
| 218 | Align268435456: 268435456, | ||
| 219 | Align536870912: 536870912, | ||
| 220 | ); | ||
| 110 | 221 | ||
| 111 | pub struct TaskPoolRef { | 222 | #[allow(dead_code)] |
| 112 | // type-erased `&'static mut TaskPool<F, N>` | 223 | pub trait HasOutput { |
| 113 | // Needed because statics can't have generics. | 224 | type Output; |
| 114 | ptr: Mutex<Cell<*mut ()>>, | ||
| 115 | } | 225 | } |
| 116 | unsafe impl Sync for TaskPoolRef {} | ||
| 117 | unsafe impl Send for TaskPoolRef {} | ||
| 118 | 226 | ||
| 119 | impl TaskPoolRef { | 227 | impl<O> HasOutput for fn() -> O { |
| 120 | pub const fn new() -> Self { | 228 | type Output = O; |
| 121 | Self { | 229 | } |
| 122 | ptr: Mutex::new(Cell::new(null_mut())), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | 230 | ||
| 126 | /// Get the pool for this ref, allocating it from the arena the first time. | 231 | #[allow(dead_code)] |
| 127 | /// | 232 | pub type Never = <fn() -> ! as HasOutput>::Output; |
| 128 | /// safety: for a given TaskPoolRef instance, must always call with the exact | 233 | } |
| 129 | /// same generic params. | ||
| 130 | pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> { | ||
| 131 | critical_section::with(|cs| { | ||
| 132 | let ptr = self.ptr.borrow(cs); | ||
| 133 | if ptr.get().is_null() { | ||
| 134 | let pool = ARENA.alloc::<TaskPool<F, N>>(cs); | ||
| 135 | pool.write(TaskPool::new()); | ||
| 136 | ptr.set(pool as *mut _ as _); | ||
| 137 | } | ||
| 138 | 234 | ||
| 139 | unsafe { &*(ptr.get() as *const _) } | 235 | /// Implementation details for embassy macros. |
| 140 | }) | 236 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. |
| 141 | } | 237 | #[doc(hidden)] |
| 238 | #[cfg(feature = "nightly")] | ||
| 239 | pub mod _export { | ||
| 240 | #[diagnostic::on_unimplemented( | ||
| 241 | message = "task futures must resolve to `()` or `!`", | ||
| 242 | note = "use `async fn` or change the return type to `impl Future<Output = ()>`" | ||
| 243 | )] | ||
| 244 | pub trait TaskReturnValue {} | ||
| 245 | impl TaskReturnValue for () {} | ||
| 246 | impl TaskReturnValue for Never {} | ||
| 247 | |||
| 248 | #[allow(dead_code)] | ||
| 249 | pub trait HasOutput { | ||
| 250 | type Output; | ||
| 251 | } | ||
| 252 | |||
| 253 | impl<O> HasOutput for fn() -> O { | ||
| 254 | type Output = O; | ||
| 142 | } | 255 | } |
| 256 | |||
| 257 | #[allow(dead_code)] | ||
| 258 | pub type Never = <fn() -> ! as HasOutput>::Output; | ||
| 143 | } | 259 | } |
