uefi_async/common/
step.rs

1//! Execution pacing and task suspension tools for UEFI asynchronous environments.
2//!
3//! This module provides three primary ways to control task execution:
4//! 1. **Yield**: Cooperatively giving up CPU time to the scheduler.
5//! 2. **Skip**: Jumping over a fixed number of scheduling cycles.
6//! 3. **Pacer**: A simple pacing mechanism that can be used to throttle task execution.
7//!
8use core::pin::Pin;
9use core::task::{Context, Poll};
10
11/// A unit structure that implements [`IntoFuture`] for a clean `.await` syntax.
12///
13/// Using `YIELD.await` is the preferred way to cooperatively yield control back
14/// to the [`Executor`]. It ensures other tasks (like input handling) can run.
15///
16/// # Example
17/// ```rust
18/// async fn heavy_computation() {
19///     for i in 0..1000 {
20///         do_work(i);
21///         // Yield every iteration to prevent system hang
22///         YIELD.await;
23///     }
24/// }
25/// ```
26pub const YIELD: _Yield = _Yield(false);
27
28/// A unit structure that implements [`IntoFuture`] for a clean `.await` syntax.
29///
30/// Using `yield_now().await` is the preferred way to cooperatively yield control back
31/// to the [`Executor`]. It ensures other tasks (like input handling) can run.
32///
33/// # Example
34/// ```rust
35/// async fn heavy_computation() {
36///     for i in 0..1000 {
37///         do_work(i);
38///         // Yield every iteration to prevent system hang
39///         yield_now().await;
40///     }
41/// }
42/// ```
43#[deprecated(since = "0.2.5", note = "Use `Yield.await` instead")]
44pub async fn yield_now() { _Yield(false).await }
45
46/// A unit structure that implements [`IntoFuture`] for a clean `.await` syntax.
47///
48/// Using `Yield.await` is the preferred way to cooperatively yield control back
49/// to the [`Executor`]. It ensures other tasks (like input handling) can run.
50///
51/// # Example
52/// ```rust
53/// async fn heavy_computation() {
54///     for i in 0..1000 {
55///         do_work(i);
56///         // Yield every iteration to prevent system hang
57///         Yield.await;
58///     }
59/// }
60/// ```
61#[derive(Debug)]
62pub struct Yield;
63impl IntoFuture for Yield {
64    type Output = ();
65    type IntoFuture = _Yield;
66    fn into_future(self) -> Self::IntoFuture { _Yield(false) }
67}
68
69/// The actual future returned by [`YIELD`] or [`Yield`].
70#[derive(Debug)]
71pub struct _Yield(bool);
72impl Future for _Yield {
73    type Output = ();
74    fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
75        if self.0 { Poll::Ready(()) } else { self.0 = true; Poll::Pending }
76    }
77}
78
79/// A future that skips a fixed number of executor polling cycles.
80///
81/// Useful for low-priority tasks that do not need to check their state
82/// on every single tick of the executor.
83///
84/// # Example
85/// ```rust
86/// async fn background_task() {
87///     loop {
88///         // Only run once every 10 scheduler passes
89///         Skip(10).await;
90///         check_background_logs();
91///     }
92/// }
93/// ```
94#[derive(Debug)]
95pub struct Skip(pub usize);
96impl Future for Skip {
97    type Output = ();
98    fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
99        if self.0 == 0 { Poll::Ready(()) } else { self.0 -= 1; Poll::Pending }
100    }
101}
102
103#[derive(Debug)]
104pub struct Pacer {
105    count: usize,
106    countdown: usize,
107}
108impl Pacer {
109    /// Creates a new `Pacer` with a specified initial count.
110    ///
111    /// # Arguments
112    /// * `count` - The number of cycles to wait before the first trigger.
113    pub fn new(count: usize) -> Self { Pacer { count, countdown: count } }
114
115    /// Updates the internal reload count and returns a mutable reference.
116    ///
117    /// Useful for dynamically adjusting task priority or execution frequency.
118    pub fn count_update(&mut self, count: usize) -> &mut Self { self.count = count; self }
119
120    /// The core generalized pacing interface.
121    ///
122    /// Decrements the counter on each call. When the counter reaches zero,
123    /// it executes an asynchronous wait (Yield or Skip).
124    ///
125    /// # Arguments
126    /// * `yield_count` - If `0`, performs a single `Yield`. If `> 0`, performs a `Skip(yield_count)`.
127    /// * `repeat` - If `true`, reloads `countdown` from `count` after triggering.
128    #[inline]
129    pub async fn step(&mut self, yield_count: usize, repeat: bool) {
130        if self.countdown > 0 { self.countdown -= 1 }
131        else {
132            if repeat { self.countdown = self.count }
133            if yield_count == 0 { Yield.await } else { Skip(yield_count).await }
134        }
135    }
136
137    /// Throttles execution until the countdown reaches zero, then yields on every subsequent call.
138    ///
139    /// Useful for "one-shot" delays or preventing a task from starting too early.
140    ///
141    /// # Example
142    /// ```rust
143    /// let mut pacer = Pacer::new(100);
144    /// loop {
145    ///     pacer.throttle().await; // Waits 100 ticks once, then yields every loop iteration.
146    ///     do_work();
147    /// }
148    /// ```
149    #[inline(always)]
150    pub async fn throttle(&mut self) { self.step(0, false).await }
151
152    /// Periodically yields control to the executor every `count` calls.
153    ///
154    /// This is the standard way to implement cooperative multitasking for background tasks.
155    ///
156    /// # Example
157    /// ```rust
158    /// let mut pacer = Pacer::new(60);
159    /// loop {
160    ///     process_physics();
161    ///     pacer.repeat().await; // Yields to other tasks once every 60 iterations.
162    /// }
163    /// ```
164    #[inline(always)]
165    pub async fn repeat(&mut self) { self.step(0, true).await }
166
167    /// Periodically yields control for multiple executor steps (`i`) every `count` calls.
168    ///
169    /// Use this for low-priority tasks that should give significant breathing room to
170    /// higher-priority tasks (like UI or Input) after a burst of work.
171    ///
172    /// # Example
173    /// ```rust
174    /// let mut pacer = Pacer::new(10);
175    /// loop {
176    ///     load_resource_chunk();
177    ///     pacer.burst(5).await; // Every 10 chunks, skip 5 scheduling passes.
178    /// }
179    /// ```
180    #[inline(always)]
181    pub async fn burst(&mut self, i: usize) { self.step(i, true).await }
182}