#![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)]
#![allow(clippy::new_without_default)]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
//! ## Feature flags
#![doc = document_features::document_features!(feature_label = r#"{feature}
"#)]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;
pub use embassy_executor_macros::task;
macro_rules! check_at_most_one {
(@amo [$($feats:literal)*] [] [$($res:tt)*]) => {
#[cfg(any($($res)*))]
compile_error!(concat!("At most one of these features can be enabled at the same time:", $(" `", $feats, "`",)*));
};
(@amo $feats:tt [$curr:literal $($rest:literal)*] [$($res:tt)*]) => {
check_at_most_one!(@amo $feats [$($rest)*] [$($res)* $(all(feature=$curr, feature=$rest),)*]);
};
($($f:literal),*$(,)?) => {
check_at_most_one!(@amo [$($f)*] [$($f)*] []);
};
}
check_at_most_one!(
"arch-avr",
"arch-cortex-m",
"arch-riscv32",
"arch-std",
"arch-wasm",
"arch-spin",
);
#[cfg(feature = "_arch")]
#[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")]
#[cfg_attr(feature = "arch-cortex-m", path = "arch/cortex_m.rs")]
#[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")]
#[cfg_attr(feature = "arch-std", path = "arch/std.rs")]
#[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")]
#[cfg_attr(feature = "arch-spin", path = "arch/spin.rs")]
mod arch;
#[cfg(feature = "_arch")]
#[allow(unused_imports)] // don't warn if the module is empty.
pub use arch::*;
pub mod raw;
mod spawner;
pub use spawner::*;
mod config {
#![allow(unused)]
include!(concat!(env!("OUT_DIR"), "/config.rs"));
}
/// Implementation details for embassy macros.
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
pub mod _export {
use core::alloc::Layout;
use core::cell::{Cell, UnsafeCell};
use core::future::Future;
use core::mem::MaybeUninit;
use core::ptr::null_mut;
use critical_section::{CriticalSection, Mutex};
use crate::raw::TaskPool;
struct Arena {
buf: UnsafeCell>,
ptr: Mutex>,
}
unsafe impl Sync for Arena {}
unsafe impl Send for Arena {}
impl Arena {
const fn new() -> Self {
Self {
buf: UnsafeCell::new(MaybeUninit::uninit()),
ptr: Mutex::new(Cell::new(null_mut())),
}
}
fn alloc(&'static self, cs: CriticalSection) -> &'static mut MaybeUninit {
let layout = Layout::new::();
let start = self.buf.get().cast::();
let end = unsafe { start.add(N) };
let mut ptr = self.ptr.borrow(cs).get();
if ptr.is_null() {
ptr = self.buf.get().cast::();
}
let bytes_left = (end as usize) - (ptr as usize);
let align_offset = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
if align_offset + layout.size() > bytes_left {
panic!("embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/");
}
let res = unsafe { ptr.add(align_offset) };
let ptr = unsafe { ptr.add(align_offset + layout.size()) };
self.ptr.borrow(cs).set(ptr);
unsafe { &mut *(res as *mut MaybeUninit) }
}
}
static ARENA: Arena<{ crate::config::TASK_ARENA_SIZE }> = Arena::new();
pub struct TaskPoolRef {
// type-erased `&'static mut TaskPool`
// Needed because statics can't have generics.
ptr: Mutex>,
}
unsafe impl Sync for TaskPoolRef {}
unsafe impl Send for TaskPoolRef {}
impl TaskPoolRef {
pub const fn new() -> Self {
Self {
ptr: Mutex::new(Cell::new(null_mut())),
}
}
/// Get the pool for this ref, allocating it from the arena the first time.
///
/// safety: for a given TaskPoolRef instance, must always call with the exact
/// same generic params.
pub unsafe fn get(&'static self) -> &'static TaskPool {
critical_section::with(|cs| {
let ptr = self.ptr.borrow(cs);
if ptr.get().is_null() {
let pool = ARENA.alloc::>(cs);
pool.write(TaskPool::new());
ptr.set(pool as *mut _ as _);
}
unsafe { &*(ptr.get() as *const _) }
})
}
}
}
| |