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#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
8
9pub(crate) mod fmt;
11
12pub use embassy_executor_macros::task;
13
14macro_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}
26check_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")]
42mod arch;
43
44#[cfg(feature = "_arch")]
45#[allow(unused_imports)] pub use arch::*;
47
48pub mod raw;
49
50mod spawner;
51pub use spawner::*;
52
53mod config {
54 #![allow(unused)]
55 include!(concat!(env!("OUT_DIR"), "/config.rs"));
56}
57
58#[doc(hidden)]
61#[cfg(not(feature = "nightly"))]
62pub 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 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 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}