uefi_async/nano_alloc/executor.rs
1use crate::{init_clock_freq, tick};
2use crate::nano_alloc::TaskNode;
3use core::task::{Context, Poll, Waker};
4
5/// A simple round-robin executor that manages a linked-list of tasks.
6///
7/// The executor polls tasks based on their scheduled `next_run_time`
8/// and supports dynamic task addition.
9pub struct Executor<'curr, 'next> {
10 /// The head of the task linked-list.
11 head: Option<&'next mut TaskNode<'curr, 'next>>,
12 /// The hardware clock frequency, used to normalize task intervals.
13 pub freq: u64,
14}
15
16impl<'curr, 'next> Executor<'curr, 'next> {
17 /// Initializes a new executor and calculates the hardware frequency.
18 #[inline]
19 pub fn new() -> Self { Self{ head: None, freq: init_clock_freq() } }
20
21 /// Adds a task node to the executor.
22 ///
23 /// This method converts the node's frequency into a tick interval
24 /// and pushes the node to the front of the linked-list.
25 #[inline]
26 pub fn add(&mut self, node: &'next mut TaskNode<'curr, 'next>) -> &mut Self {
27 // convert frequency (Hz) to interval (ticks)
28 node.interval = self.freq.checked_div(node.interval).unwrap_or(0);
29 // push to the front of the list
30 node.next = self.head.take();
31 self.head = Some(node);
32 self
33 }
34
35 /// Enters an infinite loop, continuously ticking the executor.
36 #[inline(always)]
37 pub fn run_forever(&mut self) {
38 let mut cx = Self::init_step();
39 loop { self.run_step(tick(), &mut cx) }
40 }
41
42 /// Initializes a no-op [`Context`] required for polling futures.
43 #[inline(always)]
44 pub fn init_step() -> Context<'curr> { Context::from_waker(&Waker::noop()) }
45
46 /// Performs a single pass over all registered tasks.
47 ///
48 /// For each node:
49 /// 1. Checks if the current `time` has reached `next_run_time`.
50 /// 2. Polls the future.
51 /// 3. If `Ready`, the node is removed from the list.
52 /// 4. If `Pending`, the `next_run_time` is updated by the node's interval.
53 #[inline(always)]
54 pub fn run_step(&mut self, time: u64, cx: &mut Context) {
55 let mut cursor = &mut self.head;
56
57 // traverse the task list and poll each async task
58 while let Some(node) = cursor {
59 if time >= node.next_run_time {
60 match node.future.as_mut().poll(cx) {
61 // remove it from the list by bridging the pointer when async finished.
62 // it might miss the next one, but it's not a problem for polling.
63 Poll::Ready(_) => *cursor = node.next.take(),
64 // schedule next execution time
65 Poll::Pending => node.next_run_time = time + node.interval,
66 }
67 }
68
69 // Re-borrow: not time to run yet, skip to next node
70 cursor = match { cursor } {
71 Some(node) => &mut node.next,
72 None => break,
73 };
74 }
75 }
76}