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}