Crates.io | edge-executor |
lib.rs | edge-executor |
version | 0.4.1 |
source | src |
created_at | 2022-07-30 18:15:06.343831 |
updated_at | 2023-11-09 12:50:51.721291 |
description | Async executor suitable for embedded environments. |
homepage | |
repository | https://github.com/ivmarkov/edge-executor |
max_upload_size | |
id | 635668 |
size | 68,870 |
This crate ships a minimal async executor suitable for microcontrollers and embedded systems in general.
A no_std
drop-in replacement for smol's async-executor, with the implementation being a thin wrapper around smol's async-task as well.
// 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);
}
// 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);
}));
}
// 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<LocalExecutor> = 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::<()>()));
}
no_std
(but does need alloc
):
no_std
and "no_alloc" executor, look at embassy-executor, which statically pre-allocates all tasks.core::sync::atomic
support, thanks to portable-atomic;unbounded
, yet that might mean potential allocations in an ISR context, which should be avoided).F: Future + 'static
constraints;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.:
cargo build --features portable-atomic,critical-section --no-default-features --target <your-target>