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 crate::runtime::delivery::work::validate_task_work_context()?;
129 let limit = limit.min(crate::runtime::config::DEFAULT_BATCH_LIMIT);
130 if limit == 0 {
131 return Ok(DeferredTaskWorkBatch::default());
132 }
133 let _consumer: TaskWorkConsumerGuard<'_> = self.task_work.try_claim_consumer()?;
134 let mut next_class = self.state.lock().task_work_class_cursor;
135 let outcome = (|| {
136 let mut batch = DeferredTaskWorkBatch::default();
137 let mut classes_without_progress = 0;
138 let mut scheduler_tick_retry_deferred = false;
139 while batch.processed() < limit
140 && classes_without_progress < DeferredTaskWorkClass::COUNT
141 {
142 let class = next_class;
143 next_class = class.next();
144 let processed = match class {
145 DeferredTaskWorkClass::Cancellation => {
146 let events = self.process_thread_cancellation()?;
147 batch.cancellation_events += events;
148 events
149 }
150 DeferredTaskWorkClass::Deadline => {
151 let (events, callbacks) = self.dispatch_deadline_overruns_inner(1)?;
152 batch.deadline_events += events;
153 batch.deadline_callbacks += callbacks;
154 events
155 }
156 DeferredTaskWorkClass::SchedulerTick if scheduler_tick_retry_deferred => 0,
157 DeferredTaskWorkClass::SchedulerTick => {
158 let dispatch = self.dispatch_scheduler_tick_work_inner(1)?;
159 batch.scheduler_tick_events += dispatch.events;
160 batch.scheduler_tick_callbacks += dispatch.callbacks;
161 scheduler_tick_retry_deferred |= dispatch.retry_deferred;
162 dispatch.events
163 }
164 DeferredTaskWorkClass::Exit => {
165 let callbacks = self.dispatch_exit_callbacks_inner(1)?;
166 batch.exit_callbacks += callbacks;
167 callbacks
168 }
169 DeferredTaskWorkClass::Reap => {
170 if self.reclaim_exited_execution()? {
171 batch.execution_reclaims += 1;
172 1
173 } else {
174 let reaped = self.reap_unreferenced_exited_inner(1)?;
175 batch.reaped_threads += reaped;
176 reaped
177 }
178 }
179 DeferredTaskWorkClass::Reclaim => match self.reclaim_one_resource()? {
180 ResourceReclaim::None => 0,
181 ResourceReclaim::Coroutine => {
182 batch.coroutine_reclaims += 1;
183 1
184 }
185 ResourceReclaim::AddressSpace => {
186 batch.address_space_reclaims += 1;
187 1
188 }
189 },
190 };
191 if processed == 0 {
192 classes_without_progress += 1;
193 } else {
194 classes_without_progress = 0;
195 }
196 }
197 debug_assert!(batch.processed() <= limit);
198 #[cfg(feature = "qperf-metrics")]
199 crate::diagnostics::counters::record_task_work_classes(
200 batch.deadline_events,
201 batch.scheduler_tick_events,
202 batch.exit_callbacks,
203 batch.reaped_threads,
204 batch.coroutine_reclaims,
205 batch.address_space_reclaims,
206 );
207 Ok(batch)
208 })();
209 self.state.lock().task_work_class_cursor = next_class;
210 outcome
211 }
212
213 fn dispatch_scheduler_tick_work_inner(
214 &self,
215 limit: usize,
216 ) -> Result<SchedulerTickDispatch, TaskError> {
217 let mut messages = [InboxMessage::EMPTY; crate::runtime::config::DEFAULT_BATCH_LIMIT];
218 let batch = self.deferred_scheduler_ticks.drain(
219 limit.min(crate::runtime::config::DEFAULT_BATCH_LIMIT),
220 &mut messages,
221 );
222 let mut callbacks = 0;
223 let mut retry_deferred = false;
224 for message in messages.iter().take(batch.drained()) {
225 if message.operation() != InboxOperation::SchedulerTick || message.payload() == 0 {
226 task_runtime::fatal_invariant(0x5457_0002, message.payload());
227 }
228 let core = unsafe {
229 Arc::from_raw(ptr::with_exposed_provenance::<ThreadCore>(
232 message.payload(),
233 ))
234 };
235 let _delivery = core.accept_scheduler_inbox_delivery();
236 if core.id() != message.thread_id() {
237 core.cancel_scheduler_tick_work();
238 continue;
239 }
240 let claim = core.take_scheduler_tick_work();
241 if let Some(claim) = claim
242 && let Some(extension) = core.extension_view()
243 {
244 let disposition = unsafe { claim.invoke(extension.data(), core.id()) };
249 callbacks += 1;
250 if disposition == SchedulerTickWorkDisposition::Retry {
251 retry_deferred = true;
252 if core.retry_scheduler_tick_work(&claim) {
253 self.publish_claimed_scheduler_tick_work(&core);
254 }
255 }
256 }
257 }
258 if batch.pending() {
259 self.task_work.publish();
260 }
261 Ok(SchedulerTickDispatch {
262 events: batch.drained(),
263 callbacks,
264 retry_deferred,
265 })
266 }
267
268 fn reclaim_one_resource(&self) -> Result<ResourceReclaim, TaskError> {
269 let address_space_reclaim_first = {
270 let mut state = self.state.lock();
271 let current = state.address_space_reclaim_first;
272 state.address_space_reclaim_first = !current;
273 current
274 };
275 if address_space_reclaim_first {
276 if self.reclaim_pending_address_space() {
277 return Ok(ResourceReclaim::AddressSpace);
278 }
279 self.drain_deferred_coroutine_reclaims_inner(1)
280 .map(|count| match count {
281 0 => ResourceReclaim::None,
282 1 => ResourceReclaim::Coroutine,
283 _ => unreachable!("single-resource drain exceeded its bound"),
284 })
285 } else {
286 let reclaimed = self.drain_deferred_coroutine_reclaims_inner(1)?;
287 if reclaimed != 0 {
288 Ok(ResourceReclaim::Coroutine)
289 } else if self.reclaim_pending_address_space() {
290 Ok(ResourceReclaim::AddressSpace)
291 } else {
292 Ok(ResourceReclaim::None)
293 }
294 }
295 }
296
297 fn reclaim_pending_address_space(&self) -> bool {
298 let Some(address_space) = self.state.lock().pending_address_space_reclaims.pop() else {
299 return false;
300 };
301 let handle = address_space.handle();
302 match task_runtime::destroy_address_space(handle) {
303 AddressSpaceDestroyOutcome::Released => {}
304 AddressSpaceDestroyOutcome::Active => {
305 self.state
306 .lock()
307 .pending_address_space_reclaims
308 .push(address_space);
309 match task_runtime::arm_address_space_reclaim(handle) {
310 AddressSpaceReclaimArmOutcome::Ready => self.task_work.publish(),
311 AddressSpaceReclaimArmOutcome::Armed => {}
312 }
313 return false;
314 }
315 }
316 true
317 }
318
319 fn drain_deferred_coroutine_reclaims_inner(&self, limit: usize) -> Result<usize, TaskError> {
320 const MAX_DRAIN_BATCH: usize = 64;
321
322 let mut messages = [InboxMessage::EMPTY; MAX_DRAIN_BATCH];
323 let batch = self
324 .deferred_coroutine_reclaims
325 .drain(limit.min(MAX_DRAIN_BATCH), &mut messages);
326 for message in messages.iter().take(batch.drained()) {
327 if message.operation() != InboxOperation::Reclaim || message.payload() == 0 {
328 task_runtime::fatal_invariant(0x4558_0009, message.payload());
329 }
330 let header = ptr::with_exposed_provenance_mut::<CoroutineHeader>(message.payload());
331 unsafe {
332 CoroutineHeader::deallocate_raw(header);
336 }
337 }
338 Ok(batch.drained())
339 }
340}