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 | |