embassy/embassy-executor
Dummyc0m 9e6e09a8d7 executor/spin: introduce an architecture agnostic executor
Spin polls the raw executor and never sleeps. It is useful for disabling
any power features associated with wfi/wfe-like instructions.

When implementing support for the CH32V30x MCU, the wfi instruction
had issues interacting with the USB OTG peripheral and appeared to be
non-spec-compliant.

1. When sending a USB Data-in packet, the USB peripheral appears to be
unable to read the system main memory while in WFI. This manifests in
the USB peripheral sending all or partially zeroed DATA packets.
Disabling WFI works around this issue.

2. The WFI instruction does not wake up the processor when MIE is
disabled. The MCU provides a WFITOWFE bit to emulate the WFE instruction
on arm, which, when enabled, ignores the MIE and allows the processor to
wake up. This works around the non-compliant WFI implementation.

Co-authored-by: Codetector <codetector@codetector.org>
Co-authored-by: Dummyc0m <y@types.moe>
2024-10-06 23:33:34 -07:00
..
src executor/spin: introduce an architecture agnostic executor 2024-10-06 23:33:34 -07:00
tests Fix feature flag in executor tests 2024-02-14 16:14:41 -06:00
build_common.rs Update to Rust 1.80, make check-cfg unconditional. 2024-07-25 15:53:00 +02:00
build.rs Copy build_common.rs into each crate, to make cargo publish happy 2024-05-31 08:07:51 +02:00
Cargo.toml executor/spin: introduce an architecture agnostic executor 2024-10-06 23:33:34 -07:00
CHANGELOG.md update dates 2024-08-05 08:58:56 +02:00
gen_config.py [embassy-executor] improved documentation 2023-12-22 19:05:16 +01:00
README.md [embassy-executor] improved documentation 2023-12-22 19:05:16 +01:00

embassy-executor

An async/await executor designed for embedded usage.

  • No alloc, no heap needed.
  • With nightly Rust, task futures can be fully statically allocated.
  • No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
  • Integrated timer queue: sleeping is easy, just do Timer::after_secs(1).await;.
  • No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or WFE/SEV.
  • Efficient polling: a wake will only poll the woken task, not all of them.
  • Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
  • Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.

Task arena

When the nightly Cargo feature is not enabled, embassy-executor allocates tasks out of an arena (a very simple bump allocator).

If the task arena gets full, the program will panic at runtime. To guarantee this doesn't happen, you must set the size to the sum of sizes of all tasks.

Tasks are allocated from the arena when spawned for the first time. If the task exists, the allocation is not released to the arena, but can be reused to spawn the task again. For multiple-instance tasks (like #[embassy_executor::task(pool_size = 4)]), the first spawn will allocate memory for all instances. This is done for performance and to increase predictability (for example, spawning at least 1 instance of every task at boot guarantees an immediate panic if the arena is too small, while allocating instances on-demand could delay the panic to only when the program is under load).

The arena size can be configured in two ways:

  • Via Cargo features: enable a Cargo feature like task-arena-size-8192. Only a selection of values is available, see Task Area Sizes for reference.
  • Via environment variables at build time: set the variable named EMBASSY_EXECUTOR_TASK_ARENA_SIZE. For example EMBASSY_EXECUTOR_TASK_ARENA_SIZE=4321 cargo build. You can also set them in the [env] section of .cargo/config.toml. Any value can be set, unlike with Cargo features.

Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting with different values, compilation fails.

Statically allocating tasks

When using nightly Rust, enable the nightly Cargo feature. This will make embassy-executor use the type_alias_impl_trait feature to allocate all tasks in statics. Each task gets its own static, with the exact size to hold the task (or multiple instances of it, if using pool_size) calculated automatically at compile time. If tasks don't fit in RAM, this is detected at compile time by the linker. Runtime panics due to running out of memory are not possible.

The configured arena size is ignored, no arena is used at all.