ax_task/sched/system/task_system/scheduling/
preemption.rs1use super::*;
4
5impl TaskSystem {
6 pub(super) fn commit_requested_preemption_in_rq(
7 &self,
8 mut cpu: Pin<&mut CpuLocal>,
9 mut transaction: OwnerRqTxn<'_>,
10 state: RequestedPreemptionState,
11 ) -> RequestedPreemptionCommit {
12 let next = self.pick_owner_next_after_preemption_in_rq(
13 cpu.as_mut(),
14 &mut transaction,
15 state.previous,
16 );
17 let OwnerNext {
18 core: next_core,
19 policy: next_policy_ref,
20 urgency: next_urgency,
21 } = next;
22 let next_endpoint = transaction.current_switch_endpoint().unwrap_or_else(|| {
23 task_runtime::fatal_invariant(0x5343_1206, next_core.as_ref().id().as_u64() as usize)
24 });
25 let migrated = state.migration.is_some();
26 let handoff = Self::prepare_switch_handoff(
27 state.previous,
28 state.previous_core,
29 next_core,
30 next_policy_ref,
31 PreviousSwitchDisposition::Live,
32 state.migration,
33 );
34 let reason = if migrated {
35 SwitchReason::Migrated
36 } else {
37 SwitchReason::Preempted
38 };
39 let deadline_rq_observation =
40 transaction.scheduler_deadline_rq_observation(cpu.as_ref().get_ref());
41 self.commit_owner_switch_selection(
42 cpu.as_mut(),
43 transaction,
44 handoff,
45 !migrated && !state.dispatch.has_deferred_task_lock_work(),
46 );
47 let decision =
48 Self::owner_switch_plan(state.previous_endpoint, next_endpoint, reason, state.now_ns);
49 RequestedPreemptionCommit {
50 decision,
51 previous_urgency: state.previous_urgency,
52 next_urgency,
53 dispatch: state.dispatch,
54 deadline_rq_observation,
55 }
56 }
57
58 pub(super) fn finish_requested_preemption(
59 &self,
60 mut cpu: Pin<&mut CpuLocal>,
61 commit: RequestedPreemptionCommit,
62 ) -> SchedulerOutcome {
63 self.finish_owner_dispatch_commit(commit.dispatch);
64 self.finish_owner_selection(
65 cpu.as_mut(),
66 commit.decision.previous(),
67 commit.decision.next(),
68 commit.previous_urgency,
69 commit.next_urgency,
70 OwnerSchedulerDeadline::Reevaluate(commit.deadline_rq_observation),
71 );
72 SchedulerOutcome::Decision(commit.decision)
73 }
74
75 pub(super) fn lone_realtime_preemption_keeps_dispatch(
76 &self,
77 transaction: &mut OwnerRqTxn<'_>,
78 current: &ThreadCore,
79 ) -> bool {
80 let Some(current_policy) = transaction.current().map(CurrentDispatch::schedule_policy)
81 else {
82 return false;
83 };
84 realtime_current_remains_selected(transaction, current_policy)
85 && self
86 .prepare_owner_rq_schedule_out(transaction, current)
87 .is_some()
88 }
89
90 pub(super) fn finish_owner_no_switch(
91 &self,
92 mut cpu: Pin<&mut CpuLocal>,
93 mut transaction: OwnerRqTxn<'_>,
94 current: ThreadId,
95 request_scope: SchedulerRequestScope,
96 scheduler_deadline: OwnerSchedulerDeadline,
97 ) -> Result<SchedulerOutcome, TaskError> {
98 let runtime_overrun_work = self.sync_owner_current_dispatch_in_rq(&mut transaction);
99 let request = transaction.commit_and_finish_scheduler_request();
100
101 if let Some(core) = runtime_overrun_work {
102 self.publish_deadline_overrun_work(core);
103 }
104 let run_queue_changed = if request.owner_work_requested()
105 && self.owner_balance_work_pending(cpu.as_ref().get_ref(), current)
106 {
107 self.service_owner_balance(cpu.as_mut(), current)?
108 .run_queue_changed()
109 } else {
110 false
111 };
112 match (run_queue_changed, scheduler_deadline) {
113 (true, _) => self.program_local_timer(
114 cpu.as_mut(),
115 SchedulerDeadlineDerivationSource::ScheduleNoSwitch,
116 )?,
117 (false, OwnerSchedulerDeadline::Unchanged) => {}
118 (false, OwnerSchedulerDeadline::Reevaluate(deadline_rq_observation)) => self
119 .program_local_timer_from_rq_observation(
120 cpu.as_mut(),
121 deadline_rq_observation,
122 SchedulerDeadlineDerivationSource::ScheduleNoSwitch,
123 )?,
124 }
125 Ok(
126 if cpu.scheduler_request_pending(request_scope) || cpu.has_remote_work() {
127 SchedulerOutcome::OwnerWorkPending
128 } else {
129 SchedulerOutcome::Quiescent
130 },
131 )
132 }
133
134 pub fn schedule_if_requested(
139 &self,
140 cpu: Pin<&mut CpuLocal>,
141 current: &ThreadHandle,
142 ) -> Result<SchedulerOutcome, TaskError> {
143 self.schedule_if_requested_owner(
144 cpu,
145 current.runtime_core_arc(),
146 OwnerRqEntry::IrqSave,
147 SchedulerRequestScope::All,
148 )
149 }
150
151 pub(crate) unsafe fn schedule_if_requested_in_scheduler_frame(
157 &self,
158 cpu: Pin<&mut CpuLocal>,
159 current: &CurrentThreadRef,
160 request_scope: SchedulerRequestScope,
161 ) -> Result<SchedulerOutcome, TaskError> {
162 self.schedule_if_requested_owner(
163 cpu,
164 current.runtime_core(),
165 OwnerRqEntry::SchedulerFrame,
166 request_scope,
167 )
168 }
169
170 pub(super) fn schedule_if_requested_owner(
171 &self,
172 mut cpu: Pin<&mut CpuLocal>,
173 current: &ThreadCore,
174 rq_entry: OwnerRqEntry,
175 request_scope: SchedulerRequestScope,
176 ) -> Result<SchedulerOutcome, TaskError> {
177 let validate_owner = rq_entry.requires_owner_context_validation();
178 if validate_owner {
179 self.ensure_owner_cpu_context(&cpu)?;
180 }
181 let remote = unsafe { cpu.as_ref().get_ref().remote_for_owner() };
184 let initial_request = remote.claim_scheduler_request(request_scope);
185 self.drain_owner_work(cpu.as_mut())?;
186 if validate_owner {
187 self.ensure_owner_cpu_registration_online(&cpu)?;
188 }
189 let previous_core_hint = current;
190 let mut transaction = unsafe { rq_entry.begin(self, remote) };
195 transaction.adopt_scheduler_request(initial_request);
196 if transaction.current().is_some() {
197 let _settled = transaction.settle_current(0);
198 }
199 let mut request = transaction.merge_scheduler_request(request_scope);
203 if request_scope == SchedulerRequestScope::Immediate
204 && request.immediate_preempt_requested()
205 {
206 request = transaction.merge_scheduler_request(SchedulerRequestScope::All);
210 }
211 if transaction.current_core_ref().map(ThreadCore::state) == Some(ThreadState::Parking) {
212 cpu.defer_park_preemption(request);
218 transaction.commit_and_finish_scheduler_request();
219 return Ok(SchedulerOutcome::ParkingDeferred);
220 }
221 let switch_requested = request.preemption_requested();
222 let previous = transaction.current_thread();
223 if transaction
224 .current_core_ref()
225 .is_none_or(|current| !core::ptr::eq(current, previous_core_hint))
226 {
227 task_runtime::fatal_invariant(0x5343_1204, cpu.owner().as_u32() as usize);
228 }
229 if !switch_requested
230 || self.lone_realtime_preemption_keeps_dispatch(&mut transaction, previous_core_hint)
231 {
232 let deadline_rq_observation =
233 transaction.scheduler_deadline_rq_observation(cpu.as_ref().get_ref());
234 return self.finish_owner_no_switch(
235 cpu.as_mut(),
236 transaction,
237 previous_core_hint.id(),
238 request_scope,
239 OwnerSchedulerDeadline::Reevaluate(deadline_rq_observation),
240 );
241 }
242 if let Some(schedule_out) =
243 self.prepare_owner_rq_schedule_out(&transaction, previous_core_hint)
244 {
245 let now_ns = transaction.clock().wall().as_nanos();
246 let dispatch_commit = self.sync_owner_settled_current_dispatch_in_rq(&mut transaction);
247 let OwnerRqScheduledOut {
248 core: previous_core,
249 endpoint: previous_endpoint,
250 fifo: _,
251 urgency: previous_urgency,
252 realtime_yield_head: _,
253 } = self.schedule_out_owner_rq_owned(
254 &mut transaction,
255 schedule_out,
256 EnqueueReason::Preempted,
257 );
258 let commit = self.commit_requested_preemption_in_rq(
259 cpu.as_mut(),
260 transaction,
261 RequestedPreemptionState {
262 previous,
263 previous_core: Some(previous_core),
264 previous_endpoint: Some(previous_endpoint),
265 previous_urgency: Some(previous_urgency),
266 dispatch: dispatch_commit,
267 migration: None,
268 now_ns,
269 },
270 );
271 return Ok(self.finish_requested_preemption(cpu.as_mut(), commit));
272 }
273 transaction.commit();
277
278 let mut previous_sched = unsafe { rq_entry.lock_thread_sched(previous_core_hint.sched()) };
283 let mut transaction = unsafe { rq_entry.begin(self, remote) };
285 transaction.adopt_scheduler_request(request);
286 if transaction.current().is_some() {
287 let _settled = transaction.settle_current(0);
288 }
289 let request = transaction.merge_scheduler_request(SchedulerRequestScope::All);
290 if !request.preemption_requested() {
291 task_runtime::fatal_invariant(0x5343_120a, cpu.owner().as_u32() as usize);
292 }
293 let now_ns = transaction.clock().wall().as_nanos();
294 let previous = transaction.current_thread();
295 let previous_core = transaction.current_core();
296 let previous_endpoint = transaction.current_switch_endpoint();
297 if previous_core
298 .as_ref()
299 .is_none_or(|core| !core::ptr::eq(core.as_ref(), previous_core_hint))
300 {
301 task_runtime::fatal_invariant(0x5343_1204, cpu.owner().as_u32() as usize);
302 }
303 let dispatch_commit = self.sync_owner_settled_current_dispatch_in_rq(&mut transaction);
304 let previous_urgency = transaction.current_scheduling_urgency();
305 let mut migration = None;
306 if let Some(core) = previous_core.as_ref() {
307 let schedule_out = self.schedule_out_owner_running_in_rq(
308 cpu.as_mut(),
309 &mut transaction,
310 Arc::clone(core),
311 &mut previous_sched,
312 now_ns,
313 EnqueueReason::Preempted,
314 );
315 migration = schedule_out.migration;
316 }
317 let commit = self.commit_requested_preemption_in_rq(
318 cpu.as_mut(),
319 transaction,
320 RequestedPreemptionState {
321 previous,
322 previous_core: previous_core.map(PreviousSwitchOwnership::retained),
323 previous_endpoint,
324 previous_urgency,
325 dispatch: dispatch_commit,
326 migration,
327 now_ns,
328 },
329 );
330 drop(previous_sched);
331 Ok(self.finish_requested_preemption(cpu.as_mut(), commit))
332 }
333}