Skip to main content

uefi_async/
lib.rs

1//!uefi-async
2//! ================================
3//! A lightweight, zero-cost asynchronous executor designed specifically for UEFI environments or bare-metal Rust. It provides a simple task scheduler based on a intrusive linked-list and a procedural macro to simplify task registration.
4//!
5//! WIP
6//! --------------------------------
7//! currently only `nano_alloc` feature is supported.
8//!
9//! Features
10//! --------------------------------
11//! * **No-Std Compatible**: Designed for environments without a standard library (only requires `alloc`).
12//! * **Intrusive Linked-List**: No additional collection overhead for managing tasks.
13//! * **Frequency-Based Scheduling**: Define tasks to run at specific frequencies (Hz), automatically converted to hardware ticks.
14//! * **Macro-Driven Syntax**: A clean, declarative DSL to assign tasks to executors.
15//!
16//! Architecture
17//! --------------------------------
18//! The project consists of two main components:
19//!
20//! 1. **The Executor**: A round-robin scheduler that polls `TaskNode`s based on timing requirements.
21//! 2. **The Task Macro**: A procedural macro that handles the boilerplate of pinning futures and registering them to executors.
22//!
23//! Installation
24//! --------------------------------
25//! Add this to your `Cargo.toml`:
26//!
27//! ```toml
28//! [dependencies]
29//! uefi-async = "*"
30//!
31//! ```
32//!
33//! Usage
34//! --------------------------------
35//!
36//! ### 1. Define your tasks
37//!
38//! Tasks are standard Rust `async` functions or closures.
39//!
40//! ### 2. Initialize and Run
41//!
42//! Use the `add!` macro to set up your executor.
43//!
44//! ```rust
45//! extern crate alloc;
46//! use alloc::boxed::Box;
47//! use uefi_async::nano_alloc::{Executor, TaskNode};
48//!
49//! async fn calc_1() {}
50//! async fn calc_2() {}
51//!
52//! extern "efiapi" fn process(arg: *mut c_void) {
53//!     // 1. Create executor
54//!     Executor::new()
55//!         // 2. Register tasks
56//!         .add(&mut TaskNode::new(Box::pin(calc_1()), 0))
57//!         .add(&mut TaskNode::new(Box::pin(calc_2()), 60))
58//!         // 3. Run the event loop
59//!         .run_forever();
60//! }
61//! ```
62//!
63//! or more advanced usage:
64//!
65//!```rust
66//! extern crate alloc;
67//! use uefi_async::nano_alloc::{Executor, add};
68//! use uefi_async::common::tick;
69//!
70//! async fn af1() {}
71//! async fn af2(_: usize) {}
72//! async fn af3(_: usize, _:usize) {}
73//!
74//! extern "efiapi" fn process(arg: *mut c_void) {
75//!     if arg.is_null() { return }
76//!     let ctx = unsafe { &mut *arg.cast::<Context>() };
77//!     let core = ctx.mp.who_am_i().expect("Failed to get core ID");
78//!
79//!     // 1. Create executor
80//!     let mut executor1 = Executor::new();
81//!     let mut executor2 = Executor::new();
82//!     let mut cx = Executor::init_step();
83//!
84//!     let offset = 20;
85//!     // 2. Use the macro to register tasks
86//!     // Syntax: executor => { frequency -> future }
87//!     add! (
88//!         executor1 => {
89//!             0  -> af1(),        // Runs at every tick
90//!             60 -> af2(core),    // Runs at 60 HZ
91//!         },
92//!         executor2 => {
93//!             10u64.saturating_sub(offset) -> af3(core, core),
94//!             30 + 10                      -> af1(),
95//!         },
96//!     );
97//!
98//!     loop {
99//!         calc_sync(core);
100//!
101//!         // 3. Run the event loop manually
102//!         executor1.run_step(tick(), &mut cx);
103//!         executor2.run_step(tick(), &mut cx);
104//!     }
105//! }
106//! ```
107//!
108//! Why use `uefi-async`?
109//! --------------------------------
110//! In UEFI development, managing multiple periodic tasks (like polling keyboard input while updating a UI or handling network packets) manually can lead to "spaghetti code." `uefi-async` allows you to write clean, linear `async/await` code while the executor ensures that timing constraints are met without a heavy OS-like scheduler.
111//!
112//! License
113//! --------------------------------
114//! This project is licensed under the MIT License or Apache-2.0. (temporary)
115
116#![warn(unreachable_pub)]
117#![no_std]
118#![cfg_attr(docsrs, feature(doc_cfg))]
119
120#[cfg(any(feature = "nano-alloc", feature = "alloc"))]
121extern crate alloc;
122
123/// Utility functions for hardware timing and platform-specific operations.
124///
125/// Includes the TSC-based tick counter and frequency calibration.
126pub mod common;
127pub use common::*;
128
129/// Static task management module.
130///
131/// This module provides a mechanism for running the executor without
132/// a dynamic memory allocator, utilizing static memory or stack-allocated
133/// task nodes. Useful for highly constrained environments.
134#[cfg(feature = "static")]
135pub mod no_alloc;
136
137/// Standard asynchronous executor implementation using `alloc`.
138///
139/// Provides the Executor and TaskNode types that rely on
140/// `Box` and `Pin` for flexible task management.
141/// Requires a global allocator to be defined.
142#[cfg(feature = "alloc")]
143pub mod dynamic;
144
145/// Helper module for setting up a global allocator in UEFI.
146///
147/// When enabled, this module provides a bridge between the Rust
148/// memory allocation API and the UEFI Boot Services memory allocation functions.
149#[cfg(feature = "global-allocator")]
150pub mod global_allocator;
151
152/// Specialized, lightweight memory allocator for constrained systems.
153///
154/// A minimal allocator implementation designed to have a very small
155/// footprint, specifically optimized for managing asynchronous task nodes.
156#[cfg(feature = "nano-alloc")]
157pub mod nano_alloc;