ax-runtime 0.12.0

Runtime library of ArceOS
Documentation
use ax_lazyinit::OnceLock;
use ax_task::{
    sync::irq::{IrqWaitCell, IrqWorkerWaiter},
    thread::{TaskError, ThreadId, current::current_thread_handle},
};

/// One coalescing IRQ doorbell consumed by exactly one runtime worker.
///
/// Hard IRQ owns only publication to [`IrqWaitCell`]. Scheduler state and
/// task-context fanout remain owned by the fixed worker which calls
/// [`Self::wait`].
pub struct FixedIrqWorkerSignal {
    doorbell: IrqWaitCell,
    waiter: OnceLock<FixedIrqWorkerWaiter>,
}

impl FixedIrqWorkerSignal {
    pub const fn new() -> Self {
        Self {
            doorbell: IrqWaitCell::new(),
            waiter: OnceLock::new(),
        }
    }

    /// Publishes work from task or hard IRQ context without entering
    /// task-owned wait queues.
    pub fn notify(&self) {
        let _result = self.doorbell.notify();
    }

    /// Consumes one coalesced notification on the signal's fixed worker.
    pub fn wait(&self) -> Result<(), TaskError> {
        let current = current_thread_handle()?;
        let waiter = self.waiter.call_once(|| FixedIrqWorkerWaiter {
            owner: current.id(),
            irq: IrqWorkerWaiter::new(current.wake_handle()),
        });
        if waiter.owner != current.id() {
            return Err(TaskError::InvalidConfiguration);
        }
        waiter.irq.wait(&self.doorbell)
    }

    #[cfg(test)]
    pub(crate) fn is_pending(&self) -> bool {
        self.doorbell.is_pending()
    }
}

impl Default for FixedIrqWorkerSignal {
    fn default() -> Self {
        Self::new()
    }
}

struct FixedIrqWorkerWaiter {
    owner: ThreadId,
    irq: IrqWorkerWaiter,
}