Skip to main content

ax_task/sched/system/task_system/deadline/
overrun.rs

1//! Overrun under the owning scheduler transaction.
2
3use super::*;
4
5impl TaskSystem {
6    pub(in crate::sched::system::task_system) fn publish_deadline_overrun_work(
7        &self,
8        core: Arc<ThreadCore>,
9    ) {
10        let core_ptr = Arc::into_raw(core);
11        let node = unsafe {
12            // SAFETY: Arc allocations remain pinned, and the strong count
13            // transferred below keeps the embedded node alive until drain.
14            Pin::new_unchecked((&*core_ptr).deadline_callback_node())
15        };
16        let result = self.deferred_deadline_callbacks.publish(
17            node,
18            InboxMessage::deadline_overrun(
19                unsafe {
20                    // SAFETY: the transferred Arc keeps this core alive.
21                    (*core_ptr).id()
22                },
23                core_ptr.expose_provenance(),
24            ),
25        );
26        if result == PublishResult::Published {
27            self.task_work.publish();
28            return;
29        }
30        unsafe {
31            // SAFETY: a coalesced or rejected publication did not consume the
32            // transferred strong count.
33            drop(Arc::from_raw(core_ptr));
34        }
35        if result == PublishResult::WrongKind {
36            task_runtime::fatal_invariant(0x444c_0001, result as usize);
37        }
38    }
39
40    pub(super) fn task_deadline_error(error: TaskDeadlineError) -> TaskError {
41        match error {
42            TaskDeadlineError::Capacity => TaskError::TimerCapacity,
43            TaskDeadlineError::GenerationExhausted | TaskDeadlineError::KindMismatch => {
44                TaskError::InvalidConfiguration
45            }
46        }
47    }
48
49    /// Runs a bounded, allocation-free batch of deferred Deadline callbacks.
50    ///
51    /// Timer IRQ only publishes pending state. This task-context operation drops
52    /// the registry lock before invoking any OS extension callback. Callback
53    /// collection retains one existing thread-core reference at a time instead
54    /// of allocating temporary storage in a scheduler-adjacent safe point.
55    ///
56    /// # Errors
57    ///
58    /// Returns [`TaskError::UnsafeContext`] without consuming an event in hard
59    /// IRQ context, and [`TaskError::ThreadBusy`] when another task-work
60    /// consumer is already active.
61    pub fn dispatch_deadline_overruns(&self, limit: usize) -> Result<usize, TaskError> {
62        if task_runtime::in_hard_irq() {
63            return Err(TaskError::UnsafeContext);
64        }
65        let _consumer = self.task_work.try_claim_consumer()?;
66        self.dispatch_deadline_overruns_inner(limit)
67            .map(|(_, dispatched)| dispatched)
68    }
69
70    pub(in crate::sched::system::task_system) fn dispatch_deadline_overruns_inner(
71        &self,
72        limit: usize,
73    ) -> Result<(usize, usize), TaskError> {
74        const MAX_DISPATCH_BATCH: usize = 64;
75
76        let mut messages = [InboxMessage::EMPTY; MAX_DISPATCH_BATCH];
77        let batch = self
78            .deferred_deadline_callbacks
79            .drain(limit.min(MAX_DISPATCH_BATCH), &mut messages);
80        let mut dispatched = 0;
81        for message in messages.iter().take(batch.drained()) {
82            if message.operation() != InboxOperation::DeadlineOverrun || message.payload() == 0 {
83                task_runtime::fatal_invariant(0x444c_0002, message.payload());
84            }
85            let core = unsafe {
86                // SAFETY: publication transferred exactly one Arc strong count
87                // whose pointer is carried by this detached message.
88                Arc::from_raw(ptr::with_exposed_provenance::<ThreadCore>(
89                    message.payload(),
90                ))
91            };
92            if core.id() != message.thread_id() {
93                task_runtime::fatal_invariant(0x444c_0003, message.payload());
94            }
95            let claim = self
96                .state
97                .lock()
98                .claim_pending_deadline_overrun(core.id())?;
99            match claim {
100                DeadlineCallbackClaim::NoCallback { has_more } => {
101                    if has_more {
102                        self.publish_deadline_overrun_work(Arc::clone(&core));
103                    }
104                }
105                DeadlineCallbackClaim::Callback { extension, thread } => {
106                    // SAFETY: the registry's callback claim prevents reaping
107                    // while the callback runs, and every scheduler lock was
108                    // released above.
109                    unsafe {
110                        (extension.ops().on_deadline_overrun)(extension.data(), thread);
111                    }
112                    if self.state.lock().finish_deadline_callback(thread)? {
113                        self.publish_deadline_overrun_work(Arc::clone(&core));
114                    }
115                    dispatched += 1;
116                }
117            }
118        }
119        if batch.pending() {
120            self.task_work.publish();
121        }
122        Ok((batch.drained(), dispatched))
123    }
124}