| 1 | #![cfg_attr (not(any(feature = "arch-std" , feature = "arch-wasm" )), no_std)] |
| 2 | #![allow (clippy::new_without_default)] |
| 3 | #![doc = include_str!("../README.md" )] |
| 4 | #![warn (missing_docs)] |
| 5 | |
| 6 | //! ## Feature flags |
| 7 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"# )] |
| 8 | |
| 9 | // This mod MUST go first, so that the others see its macros. |
| 10 | pub(crate) mod fmt; |
| 11 | |
| 12 | pub use embassy_executor_macros::task; |
| 13 | |
| 14 | macro_rules! check_at_most_one { |
| 15 | (@amo [$($feats:literal)*] [] [$($res:tt)*]) => { |
| 16 | #[cfg(any($($res)*))] |
| 17 | compile_error!(concat!("At most one of these features can be enabled at the same time:" , $(" `" , $feats, "`" ,)*)); |
| 18 | }; |
| 19 | (@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => { |
| 20 | check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]); |
| 21 | }; |
| 22 | ($($f:literal),*$(,)?) => { |
| 23 | check_at_most_one!(@amo [$($f)*] [$($f)*] []); |
| 24 | }; |
| 25 | } |
| 26 | check_at_most_one!( |
| 27 | "arch-avr" , |
| 28 | "arch-cortex-m" , |
| 29 | "arch-riscv32" , |
| 30 | "arch-std" , |
| 31 | "arch-wasm" , |
| 32 | "arch-spin" , |
| 33 | ); |
| 34 | |
| 35 | #[cfg (feature = "_arch" )] |
| 36 | #[cfg_attr (feature = "arch-avr" , path = "arch/avr.rs" )] |
| 37 | #[cfg_attr (feature = "arch-cortex-m" , path = "arch/cortex_m.rs" )] |
| 38 | #[cfg_attr (feature = "arch-riscv32" , path = "arch/riscv32.rs" )] |
| 39 | #[cfg_attr (feature = "arch-std" , path = "arch/std.rs" )] |
| 40 | #[cfg_attr (feature = "arch-wasm" , path = "arch/wasm.rs" )] |
| 41 | #[cfg_attr (feature = "arch-spin" , path = "arch/spin.rs" )] |
| 42 | mod arch; |
| 43 | |
| 44 | #[cfg (feature = "_arch" )] |
| 45 | #[allow (unused_imports)] // don't warn if the module is empty. |
| 46 | pub use arch::*; |
| 47 | |
| 48 | pub mod raw; |
| 49 | |
| 50 | mod spawner; |
| 51 | pub use spawner::*; |
| 52 | |
| 53 | mod config { |
| 54 | #![allow (unused)] |
| 55 | include!(concat!(env!("OUT_DIR" ) , "/config.rs" )); |
| 56 | } |
| 57 | |
| 58 | /// Implementation details for embassy macros. |
| 59 | /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. |
| 60 | #[doc (hidden)] |
| 61 | #[cfg (not(feature = "nightly" ))] |
| 62 | pub mod _export { |
| 63 | use core::alloc::Layout; |
| 64 | use core::cell::{Cell, UnsafeCell}; |
| 65 | use core::future::Future; |
| 66 | use core::mem::MaybeUninit; |
| 67 | use core::ptr::null_mut; |
| 68 | |
| 69 | use critical_section::{CriticalSection, Mutex}; |
| 70 | |
| 71 | use crate::raw::TaskPool; |
| 72 | |
| 73 | struct Arena<const N: usize> { |
| 74 | buf: UnsafeCell<MaybeUninit<[u8; N]>>, |
| 75 | ptr: Mutex<Cell<*mut u8>>, |
| 76 | } |
| 77 | |
| 78 | unsafe impl<const N: usize> Sync for Arena<N> {} |
| 79 | unsafe impl<const N: usize> Send for Arena<N> {} |
| 80 | |
| 81 | impl<const N: usize> Arena<N> { |
| 82 | const fn new() -> Self { |
| 83 | Self { |
| 84 | buf: UnsafeCell::new(MaybeUninit::uninit()), |
| 85 | ptr: Mutex::new(Cell::new(null_mut())), |
| 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 | } |
| 106 | |
| 107 | let res = unsafe { ptr.add(align_offset) }; |
| 108 | let ptr = unsafe { ptr.add(align_offset + layout.size()) }; |
| 109 | |
| 110 | self.ptr.borrow(cs).set(ptr); |
| 111 | |
| 112 | unsafe { &mut *(res as *mut MaybeUninit<T>) } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new(); |
| 117 | |
| 118 | pub struct TaskPoolRef { |
| 119 | // type-erased `&'static mut TaskPool<F, N>` |
| 120 | // Needed because statics can't have generics. |
| 121 | ptr: Mutex<Cell<*mut ()>>, |
| 122 | } |
| 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 | |
| 133 | /// Get the pool for this ref, allocating it from the arena the first time. |
| 134 | /// |
| 135 | /// safety: for a given TaskPoolRef instance, must always call with the exact |
| 136 | /// same generic params. |
| 137 | pub unsafe fn get<F: Future, const N: usize>(&'static self) -> &'static TaskPool<F, N> { |
| 138 | critical_section::with(|cs| { |
| 139 | let ptr = self.ptr.borrow(cs); |
| 140 | if ptr.get().is_null() { |
| 141 | let pool = ARENA.alloc::<TaskPool<F, N>>(cs); |
| 142 | pool.write(TaskPool::new()); |
| 143 | ptr.set(pool as *mut _ as _); |
| 144 | } |
| 145 | |
| 146 | unsafe { &*(ptr.get() as *const _) } |
| 147 | }) |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |