#async-executor #executor #async

no-std edge-executor

适用于嵌入式环境的异步执行器

7 个不稳定版本

0.4.1 2023年11月9日
0.4.0 2023年10月18日
0.3.1 2023年8月20日
0.3.0 2022年10月17日
0.1.1 2022年7月30日

#412 in 嵌入式开发

Download history 507/week @ 2024-04-28 539/week @ 2024-05-05 561/week @ 2024-05-12 531/week @ 2024-05-19 315/week @ 2024-05-26 247/week @ 2024-06-02 187/week @ 2024-06-09 278/week @ 2024-06-16 360/week @ 2024-06-23 142/week @ 2024-06-30 151/week @ 2024-07-07 232/week @ 2024-07-14 250/week @ 2024-07-21 336/week @ 2024-07-28 297/week @ 2024-08-04 163/week @ 2024-08-11

每月1,065次下载
4 个 crate 中使用 (via rbd_dimmer)

MIT/Apache

20KB
186

edge-executor

CI crates.io Documentation

本 crate 提供了一个适用于微控制器和嵌入式系统的一般环境的最小异步执行器。

这是 smolasync-executor 的 no_std 一次性替换,其实现是 smolasync-task 的薄包装。

示例

// 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 (但需要 alloc)
    • 执行器以受控的方式使用分配:仅在创建新任务时以及执行器本身构建期间;
    • 对于 no_std 和 "no_alloc" 执行器,请参阅 embassy-executor,它静态预分配所有任务。
  • 在无 core::sync::atomic 支持的目标上运行,多亏了 portable-atomic
  • 不假设 RTOS,也可以完全裸机运行;
  • 默认情况下,基于原子锁的、有界任务队列,适用于从 FreeRTOS 或 ESP-IDF 等ISR直接唤醒执行器,无界也是选项(具有 unbounded 功能,但这可能意味着 ISR 上下文中的潜在分配,应避免)。

async-executor 继承的优秀功能

  • 堆借用:在执行器上生成的 future 只需要与执行器本身存在一样长的时间。没有 F: Future + 'static 约束;
  • 完全可移植和异步。 Executor::run 简单返回一个 Future。轮询此 future 将运行执行器,即 block_on(executor.run(core::future:pending::<()>()))
  • const new 构造函数。

注意:要在没有原子操作 core 的 Rust no_std 目标(例如 riscv32imc-unknown-none-elf 和类似的单核 MCU)上编译,请启用特性 portable-atomiccritical-section。即:

cargo build --features portable-atomic,critical-section --no-default-features --target <your-target>

依赖项

~0.5-1MB
~16K SLoC