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 /embassy-executor/src/lib.rs | |
| parent | 1fbc150fd6392d8268aa35d15380c02e363c4eb8 (diff) | |
executor: add support for main/task macros in stable (allocates tasks in an arena)
Diffstat (limited to 'embassy-executor/src/lib.rs')
| -rw-r--r-- | embassy-executor/src/lib.rs | 95 |
1 files changed, 92 insertions, 3 deletions
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 | } | ||
