diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-03-28 18:59:02 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2025-03-28 19:11:53 +0100 |
| commit | 695a6da322aa2d75c8f702b2ed8b67f9ad12c3a0 (patch) | |
| tree | 06501a7878909c2d53b98d47756ff2b7e13dfcda /embassy-executor/src/lib.rs | |
| parent | bda7ba7b149b19f1cb67c5f664ceaa10f071dce7 (diff) | |
Statically allocate task pools on stable Rust.
Thanks @0e4ef622 for the awesome idea of how to do it and the first implementation.
Co-Authored-By: Matthew Tran <[email protected]>
Diffstat (limited to 'embassy-executor/src/lib.rs')
| -rw-r--r-- | embassy-executor/src/lib.rs | 178 |
1 files changed, 103 insertions, 75 deletions
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index d816539ac..5485f6a6a 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -50,101 +50,129 @@ pub mod raw; | |||
| 50 | mod spawner; | 50 | mod spawner; |
| 51 | pub use spawner::*; | 51 | pub use spawner::*; |
| 52 | 52 | ||
| 53 | mod config { | ||
| 54 | #![allow(unused)] | ||
| 55 | include!(concat!(env!("OUT_DIR"), "/config.rs")); | ||
| 56 | } | ||
| 57 | |||
| 58 | /// Implementation details for embassy macros. | 53 | /// Implementation details for embassy macros. |
| 59 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. | 54 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. |
| 60 | #[doc(hidden)] | 55 | #[doc(hidden)] |
| 61 | #[cfg(not(feature = "nightly"))] | 56 | #[cfg(not(feature = "nightly"))] |
| 62 | pub mod _export { | 57 | pub mod _export { |
| 63 | use core::alloc::Layout; | 58 | use core::cell::UnsafeCell; |
| 64 | use core::cell::{Cell, UnsafeCell}; | ||
| 65 | use core::future::Future; | 59 | use core::future::Future; |
| 66 | use core::mem::MaybeUninit; | 60 | use core::mem::MaybeUninit; |
| 67 | use core::ptr::null_mut; | ||
| 68 | |||
| 69 | use critical_section::{CriticalSection, Mutex}; | ||
| 70 | 61 | ||
| 71 | use crate::raw::TaskPool; | 62 | pub trait TaskFn<Args>: Copy { |
| 72 | 63 | type Fut: Future + 'static; | |
| 73 | struct Arena<const N: usize> { | ||
| 74 | buf: UnsafeCell<MaybeUninit<[u8; N]>>, | ||
| 75 | ptr: Mutex<Cell<*mut u8>>, | ||
| 76 | } | 64 | } |
| 77 | 65 | ||
| 78 | unsafe impl<const N: usize> Sync for Arena<N> {} | 66 | macro_rules! task_fn_impl { |
| 79 | unsafe impl<const N: usize> Send for Arena<N> {} | 67 | ($($Tn:ident),*) => { |
| 80 | 68 | impl<F, Fut, $($Tn,)*> TaskFn<($($Tn,)*)> for F | |
| 81 | impl<const N: usize> Arena<N> { | 69 | where |
| 82 | const fn new() -> Self { | 70 | F: Copy + FnOnce($($Tn,)*) -> Fut, |
| 83 | Self { | 71 | Fut: Future + 'static, |
| 84 | buf: UnsafeCell::new(MaybeUninit::uninit()), | 72 | { |
| 85 | ptr: Mutex::new(Cell::new(null_mut())), | 73 | type Fut = Fut; |
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | fn alloc<T>(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit<T> { | ||
| 90 | let layout = Layout::new::<T>(); | ||
| 91 | |||
| 92 | let start = self.buf.get().cast::<u8>(); | ||
| 93 | let end = unsafe { start.add(N) }; | ||
| 94 | |||
| 95 | let mut ptr = self.ptr.borrow(cs).get(); | ||
| 96 | if ptr.is_null() { | ||
| 97 | ptr = self.buf.get().cast::<u8>(); | ||
| 98 | } | ||
| 99 | |||
| 100 | let bytes_left = (end as usize) - (ptr as usize); | ||
| 101 | let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize); | ||
| 102 | |||
| 103 | if align_offset + layout.size() > bytes_left { | ||
| 104 | panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/"); | ||
| 105 | } | 74 | } |
| 75 | }; | ||
| 76 | } | ||
| 106 | 77 | ||
| 107 | let res = unsafe { ptr.add(align_offset) }; | 78 | task_fn_impl!(); |
| 108 | let ptr = unsafe { ptr.add(align_offset + layout.size()) }; | 79 | task_fn_impl!(T0); |
| 80 | task_fn_impl!(T0, T1); | ||
| 81 | task_fn_impl!(T0, T1, T2); | ||
| 82 | task_fn_impl!(T0, T1, T2, T3); | ||
| 83 | task_fn_impl!(T0, T1, T2, T3, T4); | ||
| 84 | task_fn_impl!(T0, T1, T2, T3, T4, T5); | ||
| 85 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6); | ||
| 86 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7); | ||
| 87 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8); | ||
| 88 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9); | ||
| 89 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); | ||
| 90 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); | ||
| 91 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); | ||
| 92 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); | ||
| 93 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); | ||
| 94 | task_fn_impl!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); | ||
| 95 | |||
| 96 | #[allow(private_bounds)] | ||
| 97 | #[repr(C)] | ||
| 98 | pub struct TaskPoolHolder<const SIZE: usize, const ALIGN: usize> | ||
| 99 | where | ||
| 100 | Align<ALIGN>: Alignment, | ||
| 101 | { | ||
| 102 | data: UnsafeCell<[MaybeUninit<u8>; SIZE]>, | ||
| 103 | align: Align<ALIGN>, | ||
| 104 | } | ||
| 109 | 105 | ||
| 110 | self.ptr.borrow(cs).set(ptr); | 106 | unsafe impl<const SIZE: usize, const ALIGN: usize> Send for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {} |
| 107 | unsafe impl<const SIZE: usize, const ALIGN: usize> Sync for TaskPoolHolder<SIZE, ALIGN> where Align<ALIGN>: Alignment {} | ||
| 111 | 108 | ||
| 112 | unsafe { &mut *(res as *mut MaybeUninit<T>) } | 109 | #[allow(private_bounds)] |
| 110 | impl<const SIZE: usize, const ALIGN: usize> TaskPoolHolder<SIZE, ALIGN> | ||
| 111 | where | ||
| 112 | Align<ALIGN>: Alignment, | ||
| 113 | { | ||
| 114 | pub const fn get(&self) -> *const u8 { | ||
| 115 | self.data.get().cast() | ||
| 113 | } | 116 | } |
| 114 | } | 117 | } |
| 115 | 118 | ||
| 116 | static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new(); | 119 | #[allow(private_bounds)] |
| 120 | #[repr(transparent)] | ||
| 121 | pub struct Align<const N: usize>([<Self as Alignment>::Archetype; 0]) | ||
| 122 | where | ||
| 123 | Self: Alignment; | ||
| 117 | 124 | ||
| 118 | pub struct TaskPoolRef { | 125 | trait Alignment { |
| 119 | // type-erased `&'static mut TaskPool<F, N>` | 126 | /// A zero-sized type of particular alignment. |
| 120 | // Needed because statics can't have generics. | 127 | type Archetype: Copy + Eq + PartialEq + Send + Sync + Unpin; |
| 121 | ptr: Mutex<Cell<*mut ()>>, | ||
| 122 | } | 128 | } |
| 123 | unsafe impl Sync for TaskPoolRef {} | ||
| 124 | unsafe impl Send for TaskPoolRef {} | ||
| 125 | |||
| 126 | impl TaskPoolRef { | ||
| 127 | pub const fn new() -> Self { | ||
| 128 | Self { | ||
| 129 | ptr: Mutex::new(Cell::new(null_mut())), | ||
| 130 | } | ||
| 131 | } | ||
| 132 | 129 | ||
| 133 | /// Get the pool for this ref, allocating it from the arena the first time. | 130 | macro_rules! aligns { |
| 134 | /// | 131 | ($($AlignX:ident: $n:literal,)*) => { |
| 135 | /// safety: for a given TaskPoolRef instance, must always call with the exact | 132 | $( |
| 136 | /// same generic params. | 133 | #[derive(Copy, Clone, Eq, PartialEq)] |
| 137 | pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> { | 134 | #[repr(align($n))] |
| 138 | critical_section::with(|cs| { | 135 | struct $AlignX {} |
| 139 | let ptr = self.ptr.borrow(cs); | 136 | impl Alignment for Align<$n> { |
| 140 | if ptr.get().is_null() { | 137 | type Archetype = $AlignX; |
| 141 | let pool = ARENA.alloc::<TaskPool<F, N>>(cs); | ||
| 142 | pool.write(TaskPool::new()); | ||
| 143 | ptr.set(pool as *mut _ as _); | ||
| 144 | } | 138 | } |
| 145 | 139 | )* | |
| 146 | unsafe { &*(ptr.get() as *const _) } | 140 | }; |
| 147 | }) | ||
| 148 | } | ||
| 149 | } | 141 | } |
| 142 | |||
| 143 | aligns!( | ||
| 144 | Align1: 1, | ||
| 145 | Align2: 2, | ||
| 146 | Align4: 4, | ||
| 147 | Align8: 8, | ||
| 148 | Align16: 16, | ||
| 149 | Align32: 32, | ||
| 150 | Align64: 64, | ||
| 151 | Align128: 128, | ||
| 152 | Align256: 256, | ||
| 153 | Align512: 512, | ||
| 154 | Align1024: 1024, | ||
| 155 | Align2048: 2048, | ||
| 156 | Align4096: 4096, | ||
| 157 | Align8192: 8192, | ||
| 158 | Align16384: 16384, | ||
| 159 | ); | ||
| 160 | #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] | ||
| 161 | aligns!( | ||
| 162 | Align32768: 32768, | ||
| 163 | Align65536: 65536, | ||
| 164 | Align131072: 131072, | ||
| 165 | Align262144: 262144, | ||
| 166 | Align524288: 524288, | ||
| 167 | Align1048576: 1048576, | ||
| 168 | Align2097152: 2097152, | ||
| 169 | Align4194304: 4194304, | ||
| 170 | Align8388608: 8388608, | ||
| 171 | Align16777216: 16777216, | ||
| 172 | Align33554432: 33554432, | ||
| 173 | Align67108864: 67108864, | ||
| 174 | Align134217728: 134217728, | ||
| 175 | Align268435456: 268435456, | ||
| 176 | Align536870912: 536870912, | ||
| 177 | ); | ||
| 150 | } | 178 | } |
