Skip to main content

ax_runtime/irq/
worker.rs

1use ax_lazyinit::OnceLock;
2use ax_task::{
3    sync::irq::{IrqWaitCell, IrqWorkerWaiter},
4    thread::{TaskError, ThreadId, current::current_thread_handle},
5};
6
7/// One coalescing IRQ doorbell consumed by exactly one runtime worker.
8///
9/// Hard IRQ owns only publication to [`IrqWaitCell`]. Scheduler state and
10/// task-context fanout remain owned by the fixed worker which calls
11/// [`Self::wait`].
12pub struct FixedIrqWorkerSignal {
13    doorbell: IrqWaitCell,
14    waiter: OnceLock<FixedIrqWorkerWaiter>,
15}
16
17impl FixedIrqWorkerSignal {
18    pub const fn new() -> Self {
19        Self {
20            doorbell: IrqWaitCell::new(),
21            waiter: OnceLock::new(),
22        }
23    }
24
25    /// Publishes work from task or hard IRQ context without entering
26    /// task-owned wait queues.
27    pub fn notify(&self) {
28        let _result = self.doorbell.notify();
29    }
30
31    /// Consumes one coalesced notification on the signal's fixed worker.
32    pub fn wait(&self) -> Result<(), TaskError> {
33        let current = current_thread_handle()?;
34        let waiter = self.waiter.call_once(|| FixedIrqWorkerWaiter {
35            owner: current.id(),
36            irq: IrqWorkerWaiter::new(current.wake_handle()),
37        });
38        if waiter.owner != current.id() {
39            return Err(TaskError::InvalidConfiguration);
40        }
41        waiter.irq.wait(&self.doorbell)
42    }
43
44    #[cfg(test)]
45    pub(crate) fn is_pending(&self) -> bool {
46        self.doorbell.is_pending()
47    }
48}
49
50impl Default for FixedIrqWorkerSignal {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56struct FixedIrqWorkerWaiter {
57    owner: ThreadId,
58    irq: IrqWorkerWaiter,
59}