Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ivmarkov/edge-executor
A minimal async executor suitable for embedded environments
https://github.com/ivmarkov/edge-executor
Last synced: 2 months ago
JSON representation
A minimal async executor suitable for embedded environments
- Host: GitHub
- URL: https://github.com/ivmarkov/edge-executor
- Owner: ivmarkov
- License: apache-2.0
- Created: 2022-07-30T16:32:50.000Z (over 2 years ago)
- Default Branch: master
- Last Pushed: 2023-11-09T12:50:56.000Z (about 1 year ago)
- Last Synced: 2024-10-25T20:16:12.466Z (2 months ago)
- Language: Rust
- Homepage:
- Size: 83 KB
- Stars: 37
- Watchers: 3
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE-APACHE
Awesome Lists containing this project
README
# edge-executor
[![CI](https://github.com/ivmarkov/edge-executor/actions/workflows/ci.yml/badge.svg)](https://github.com/ivmarkov/edge-executor/actions/workflows/ci.yml)
![crates.io](https://img.shields.io/crates/v/edge-executor.svg)
[![Documentation](https://docs.rs/edge-executor/badge.svg)](https://docs.rs/edge-executor)This crate ships a minimal async executor suitable for microcontrollers and embedded systems in general.
A `no_std` drop-in replacement for [smol](https://github.com/smol-rs/smol)'s [async-executor](https://github.com/smol-rs/async-executor), with the implementation being a thin wrapper around [smol](https://github.com/smol-rs/smol)'s [async-task](https://github.com/smol-rs/async-task) as well.
## Examples
```rust
// ESP-IDF example, local execution, local borrows.
// With STD enabled, you can also just use `edge_executor::block_on`
// instead of `esp_idf_svc::hal::task::block_on`.use edge_executor::LocalExecutor;
use esp_idf_svc::hal::task::block_on;fn main() {
let local_ex: LocalExecutor = Default::default();// Borrowed by `&mut` inside the future spawned on the executor
let mut data = 3;let data = &mut data;
let task = local_ex.spawn(async move {
*data += 1;*data
});let res = block_on(local_ex.run(async { task.await * 2 }));
assert_eq!(res, 8);
}
``````rust
// STD example, work-stealing execution.use async_channel::unbounded;
use easy_parallel::Parallel;use edge_executor::{Executor, block_on};
fn main() {
let ex: Executor = Default::default();
let (signal, shutdown) = unbounded::<()>();Parallel::new()
// Run four executor threads.
.each(0..4, |_| block_on(ex.run(shutdown.recv())))
// Run the main future on the current thread.
.finish(|| block_on(async {
println!("Hello world!");
drop(signal);
}));
}
``````rust
// WASM example.use log::{info, Level};
use edge_executor::LocalExecutor;
use static_cell::StaticCell;
use wasm_bindgen_futures::spawn_local;use gloo_timers::future::TimeoutFuture;
static LOCAL_EX: StaticCell = StaticCell::new();
fn main() {
console_log::init_with_level(Level::Info).unwrap();// Local executor (futures can be `!Send`) yet `'static`
let local_ex = &*LOCAL_EX.init(Default::default());local_ex
.spawn(async {
loop {
info!("Tick");
TimeoutFuture::new(1000).await;
}
})
.detach();spawn_local(local_ex.run(core::future::pending::<()>()));
}
```## Highlights
- `no_std` (but does need `alloc`):
- The executor uses allocations in a controlled way: only when a new task is being spawn, as well as during the construction of the executor itself;
- For a `no_std` *and* "no_alloc" executor, look at [embassy-executor](https://github.com/embassy-rs/embassy/tree/main/embassy-executor), which statically pre-allocates all tasks.
- Works on targets which have no `core::sync::atomic` support, thanks to [portable-atomic](https://github.com/taiki-e/portable-atomic);
- Does not assume an RTOS and can run completely bare-metal too;
- Lockless, atomic-based, bounded task queue by default, which works well for waking the executor directly from an ISR on e.g. FreeRTOS or ESP-IDF (unbounded also an option with feature `unbounded`, yet that might mean potential allocations in an ISR context, which should be avoided).### Great features carried over from [async-executor](https://github.com/smol-rs/async-executor):
- Stack borrows: futures spawned on the executor need to live only as long as the executor itself. No `F: Future + 'static` constraints;
- Completely portable and async. `Executor::run` simply returns a `Future`. Polling this future runs the executor, i.e. `block_on(executor.run(core::future:pending::<()>()))`;
- `const new` constructor function.---
**NOTE**:
To compile on `no_std` targets that do **not** have atomics in Rust `core` (i.e. `riscv32imc-unknown-none-elf` and similar single-core MCUs),
enable features `portable-atomic` and `critical-section`. I.e.:
```sh
cargo build --features portable-atomic,critical-section --no-default-features --target
```
---