ax_task/sched/system/task_system/
deferred_work.rs1use super::*;
4use crate::runtime::service::{SchedulerTickMode, SchedulerTickWorkDisposition};
5
6#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
7struct SchedulerTickDispatch {
8 events: usize,
9 callbacks: usize,
10 retry_deferred: bool,
11}
12
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14enum ResourceReclaim {
15 None,
16 Coroutine,
17 AddressSpace,
18}
19
20impl TaskSystem {
21 pub(crate) fn publish_current_scheduler_tick_work(
22 &self,
23 cpu: &CpuLocal,
24 expected: ThreadId,
25 observed_ns: u64,
26 mode: SchedulerTickMode,
27 tick_ns: u64,
28 ) -> Result<(), TaskError> {
29 let Some(core) = cpu.current_core() else {
30 return Err(TaskError::NoRunnableThread);
31 };
32 if core.id() != expected {
33 return Err(TaskError::StaleThreadId);
34 }
35 core.sample_scheduler_tick_cpu_time(mode, tick_ns);
36 self.publish_scheduler_tick_work(&core, observed_ns);
37 Ok(())
38 }
39
40 fn publish_scheduler_tick_work(&self, core: &Arc<ThreadCore>, observed_ns: u64) {
41 if !core.begin_scheduler_tick_work(observed_ns) {
42 return;
43 }
44 self.publish_claimed_scheduler_tick_work(core);
45 }
46
47 fn publish_claimed_scheduler_tick_work(&self, core: &Arc<ThreadCore>) {
48 if !core.reserve_scheduler_inbox_delivery() {
49 core.cancel_scheduler_tick_work();
50 return;
51 }
52
53 let core_ptr = Arc::as_ptr(core);
54 unsafe { Arc::increment_strong_count(core_ptr) };
57 let node = unsafe { Pin::new_unchecked((&*core_ptr).scheduler_tick_work_node()) };
60 let result = self.deferred_scheduler_ticks.publish(
61 node,
62 InboxMessage::scheduler_tick(core.id(), core_ptr.expose_provenance()),
63 );
64 if result == PublishResult::Published {
65 self.task_work.publish();
66 return;
67 }
68
69 unsafe { Arc::decrement_strong_count(core_ptr) };
71 core.cancel_scheduler_inbox_delivery();
72 core.cancel_scheduler_tick_work();
73 task_runtime::fatal_invariant(0x5457_0001, result as usize);
74 }
75
76 pub(crate) fn publish_deferred_coroutine_reclaim(
78 &self,
79 header: Pin<&'static CoroutineHeader>,
80 ) -> PublishResult {
81 let data = header.address();
82 let _publication = IrqScope::enter();
83 let result = self.deferred_coroutine_reclaims.publish(
84 header.reclaim_node(),
85 InboxMessage::reclaim(ThreadId::from_parts(0, 0), 0, data),
86 );
87 if result == PublishResult::Published {
88 self.task_work.publish();
89 }
90 result
91 }
92
93 pub(crate) fn task_work_doorbell(&self) -> Arc<TaskWorkDoorbell> {
94 Arc::clone(&self.task_work)
95 }
96
97 pub(crate) fn begin_task_work_worker_install(&self) -> Result<(), TaskError> {
98 self.task_work.begin_worker_install()
99 }
100
101 pub(crate) fn finish_task_work_worker_install(&self) {
102 self.task_work.finish_worker_install();
103 }
104
105 pub(crate) fn cancel_task_work_worker_install(&self) {
106 self.task_work.cancel_worker_install();
107 }
108
109 pub fn deferred_task_work_pending(&self) -> bool {
111 self.task_work.is_pending()
112 }
113
114 pub(crate) fn publish_resource_release_ready(&self) {
115 self.task_work.publish();
116 }
117
118 pub fn service_deferred_task_work(
125 &self,
126 limit: usize,
127 ) -> Result<DeferredTaskWorkBatch, TaskError> {
128 if task_runtime::in_hard_irq() {
129 return Err(TaskError::UnsafeContext);
130 }
131 let limit = limit.min(crate::runtime::config::DEFAULT_BATCH_LIMIT);
132 if limit == 0 {
133 return Ok(DeferredTaskWorkBatch::default());
134 }
135 let _consumer: TaskWorkConsumerGuard<'_> = self.task_work.try_claim_consumer()?;
136 let mut next_class = self.state.lock().task_work_class_cursor;
137 let outcome = (|| {
138 let mut batch = DeferredTaskWorkBatch::default();
139 let mut classes_without_progress = 0;
140 let mut scheduler_tick_retry_deferred = false;
141 while batch.processed() < limit
142 && classes_without_progress < DeferredTaskWorkClass::COUNT
143 {
144 let class = next_class;
145 next_class = class.next();
146 let processed = match class {
147 DeferredTaskWorkClass::Deadline => {
148 let (events, callbacks) = self.dispatch_deadline_overruns_inner(1)?;
149 batch.deadline_events += events;
150 batch.deadline_callbacks += callbacks;
151 events
152 }
153 DeferredTaskWorkClass::SchedulerTick if scheduler_tick_retry_deferred => 0,
154 DeferredTaskWorkClass::SchedulerTick => {
155 let dispatch = self.dispatch_scheduler_tick_work_inner(1)?;
156 batch.scheduler_tick_events += dispatch.events;
157 batch.scheduler_tick_callbacks += dispatch.callbacks;
158 scheduler_tick_retry_deferred |= dispatch.retry_deferred;
159 dispatch.events
160 }
161 DeferredTaskWorkClass::Exit => {
162 let callbacks = self.dispatch_exit_callbacks_inner(1)?;
163 batch.exit_callbacks += callbacks;
164 callbacks
165 }
166 DeferredTaskWorkClass::Reap => {
167 let reaped = self.reap_unreferenced_exited_inner(1)?;
168 batch.reaped_threads += reaped;
169 reaped
170 }
171 DeferredTaskWorkClass::Reclaim => match self.reclaim_one_resource()? {
172 ResourceReclaim::None => 0,
173 ResourceReclaim::Coroutine => {
174 batch.coroutine_reclaims += 1;
175 1
176 }
177 ResourceReclaim::AddressSpace => {
178 batch.address_space_reclaims += 1;
179 1
180 }
181 },
182 };
183 if processed == 0 {
184 classes_without_progress += 1;
185 } else {
186 classes_without_progress = 0;
187 }
188 }
189 debug_assert!(batch.processed() <= limit);
190 #[cfg(feature = "qperf-metrics")]
191 crate::diagnostics::counters::record_task_work_classes(
192 batch.deadline_events,
193 batch.scheduler_tick_events,
194 batch.exit_callbacks,
195 batch.reaped_threads,
196 batch.coroutine_reclaims,
197 batch.address_space_reclaims,
198 );
199 Ok(batch)
200 })();
201 self.state.lock().task_work_class_cursor = next_class;
202 outcome
203 }
204
205 fn dispatch_scheduler_tick_work_inner(
206 &self,
207 limit: usize,
208 ) -> Result<SchedulerTickDispatch, TaskError> {
209 let mut messages = [InboxMessage::EMPTY; crate::runtime::config::DEFAULT_BATCH_LIMIT];
210 let batch = self.deferred_scheduler_ticks.drain(
211 limit.min(crate::runtime::config::DEFAULT_BATCH_LIMIT),
212 &mut messages,
213 );
214 let mut callbacks = 0;
215 let mut retry_deferred = false;
216 for message in messages.iter().take(batch.drained()) {
217 if message.operation() != InboxOperation::SchedulerTick || message.payload() == 0 {
218 task_runtime::fatal_invariant(0x5457_0002, message.payload());
219 }
220 let core = unsafe {
221 Arc::from_raw(ptr::with_exposed_provenance::<ThreadCore>(
224 message.payload(),
225 ))
226 };
227 let _delivery = core.accept_scheduler_inbox_delivery();
228 if core.id() != message.thread_id() {
229 core.cancel_scheduler_tick_work();
230 continue;
231 }
232 let claim = core.take_scheduler_tick_work();
233 if let Some(claim) = claim
234 && let Some(extension) = core.extension_view()
235 {
236 let disposition = unsafe { claim.invoke(extension.data(), core.id()) };
241 callbacks += 1;
242 if disposition == SchedulerTickWorkDisposition::Retry {
243 retry_deferred = true;
244 if core.retry_scheduler_tick_work(&claim) {
245 self.publish_claimed_scheduler_tick_work(&core);
246 }
247 }
248 }
249 }
250 if batch.pending() {
251 self.task_work.publish();
252 }
253 Ok(SchedulerTickDispatch {
254 events: batch.drained(),
255 callbacks,
256 retry_deferred,
257 })
258 }
259
260 fn reclaim_one_resource(&self) -> Result<ResourceReclaim, TaskError> {
261 let address_space_reclaim_first = {
262 let mut state = self.state.lock();
263 let current = state.address_space_reclaim_first;
264 state.address_space_reclaim_first = !current;
265 current
266 };
267 if address_space_reclaim_first {
268 if self.reclaim_pending_address_space() {
269 return Ok(ResourceReclaim::AddressSpace);
270 }
271 self.drain_deferred_coroutine_reclaims_inner(1)
272 .map(|count| match count {
273 0 => ResourceReclaim::None,
274 1 => ResourceReclaim::Coroutine,
275 _ => unreachable!("single-resource drain exceeded its bound"),
276 })
277 } else {
278 let reclaimed = self.drain_deferred_coroutine_reclaims_inner(1)?;
279 if reclaimed != 0 {
280 Ok(ResourceReclaim::Coroutine)
281 } else if self.reclaim_pending_address_space() {
282 Ok(ResourceReclaim::AddressSpace)
283 } else {
284 Ok(ResourceReclaim::None)
285 }
286 }
287 }
288
289 fn reclaim_pending_address_space(&self) -> bool {
290 let Some(address_space) = self.state.lock().pending_address_space_reclaims.pop() else {
291 return false;
292 };
293 let handle = address_space.handle();
294 match task_runtime::destroy_address_space(handle) {
295 AddressSpaceDestroyOutcome::Released => {}
296 AddressSpaceDestroyOutcome::Active => {
297 self.state
298 .lock()
299 .pending_address_space_reclaims
300 .push(address_space);
301 match task_runtime::arm_address_space_reclaim(handle) {
302 AddressSpaceReclaimArmOutcome::Ready => self.task_work.publish(),
303 AddressSpaceReclaimArmOutcome::Armed => {}
304 }
305 return false;
306 }
307 }
308 true
309 }
310
311 fn drain_deferred_coroutine_reclaims_inner(&self, limit: usize) -> Result<usize, TaskError> {
312 const MAX_DRAIN_BATCH: usize = 64;
313
314 let mut messages = [InboxMessage::EMPTY; MAX_DRAIN_BATCH];
315 let batch = self
316 .deferred_coroutine_reclaims
317 .drain(limit.min(MAX_DRAIN_BATCH), &mut messages);
318 for message in messages.iter().take(batch.drained()) {
319 if message.operation() != InboxOperation::Reclaim || message.payload() == 0 {
320 task_runtime::fatal_invariant(0x4558_0009, message.payload());
321 }
322 let header = ptr::with_exposed_provenance_mut::<CoroutineHeader>(message.payload());
323 unsafe {
324 CoroutineHeader::deallocate_raw(header);
328 }
329 }
330 Ok(batch.drained())
331 }
332}