Skip to main content

ax_task/sync/irq/
worker.rs

1use core::time::Duration;
2
3use crate::{
4    runtime::service::reclaim::quiesce_irq_wait,
5    sync::{
6        WaitQueue,
7        irq::{IrqRegisterResult, IrqWaitCell, IrqWaitRegistration},
8    },
9    thread::{
10        TaskError, ThreadWakeHandle,
11        current::park::{CurrentParkStart, begin_current_park},
12    },
13};
14
15/// Fixed-owner waiter for one hard-IRQ notification cell.
16///
17/// The IRQ registration and the scheduler park transaction are the only two
18/// ownership edges. The waiter deliberately does not enqueue the same thread
19/// in a second task wait queue: a direct IRQ wake already targets the park
20/// generation owned by the scheduler.
21#[derive(Debug)]
22pub struct IrqWorkerWaiter {
23    registration: IrqWaitRegistration,
24}
25
26impl IrqWorkerWaiter {
27    /// Binds a reusable IRQ registration to one fixed scheduler thread.
28    pub fn new(wake_owner: ThreadWakeHandle) -> Self {
29        Self {
30            registration: IrqWaitRegistration::new(wake_owner),
31        }
32    }
33
34    /// Waits until the cell consumes one pending or concurrent notification.
35    ///
36    /// Unrelated scheduler wakes are retried without republishing the IRQ
37    /// registration. This is the same fixed-waiter ownership used by Linux
38    /// completion workers: the producer wakes the scheduler task directly and
39    /// the scheduler remains the sole owner of its blocked/runnable state.
40    pub fn wait(&self, event: &IrqWaitCell) -> Result<(), TaskError> {
41        match event.register(&self.registration) {
42            IrqRegisterResult::Occupied => Err(TaskError::InvalidConfiguration),
43            IrqRegisterResult::ConsumedPending => Ok(()),
44            IrqRegisterResult::Registered(token)
45            | IrqRegisterResult::NotificationInFlight(token) => loop {
46                if !token.is_attached() {
47                    return quiesce_irq_wait(token);
48                }
49                match begin_current_park()? {
50                    CurrentParkStart::Notified => {}
51                    CurrentParkStart::Prepared(park) => {
52                        let _resume = park.commit()?;
53                    }
54                }
55            },
56        }
57    }
58
59    /// Waits until notification or a relative timeout expires.
60    ///
61    /// The timeout path retains the generic wait-queue race arbitration until
62    /// the IRQ registration exposes a typed notify-versus-timeout outcome.
63    /// The unbounded completion hot path uses [`Self::wait`] and owns no
64    /// detached task wait queue.
65    pub fn wait_timeout(&self, event: &IrqWaitCell, timeout: Duration) -> Result<bool, TaskError> {
66        match event.register(&self.registration) {
67            IrqRegisterResult::Occupied => Err(TaskError::InvalidConfiguration),
68            IrqRegisterResult::ConsumedPending => Ok(false),
69            IrqRegisterResult::Registered(token)
70            | IrqRegisterResult::NotificationInFlight(token) => {
71                let park = WaitQueue::new();
72                let timed_out = park.wait_timeout_until(timeout, || !token.is_attached());
73                quiesce_irq_wait(token)?;
74                Ok(timed_out)
75            }
76        }
77    }
78}