ax_task/sched/system/task_system/park_exit/
exit.rs1use super::*;
4
5impl TaskSystem {
6 pub(crate) fn prepare_current_exit(
9 &self,
10 cpu: Pin<&mut CpuLocal>,
11 current: &ThreadHandle,
12 ) -> Result<CurrentExitPermit, TaskError> {
13 self.prepare_current_exit_inner(cpu, current, true)
14 }
15
16 pub(in crate::sched::system::task_system) fn prepare_current_exit_inner(
17 &self,
18 mut cpu: Pin<&mut CpuLocal>,
19 current: &ThreadHandle,
20 require_runtime_context: bool,
21 ) -> Result<CurrentExitPermit, TaskError> {
22 self.ensure_owner_cpu_context(&cpu)?;
23 self.drain_owner_work(cpu.as_mut())?;
24 let current_id = current.id();
25 if cpu.remote().idle_thread() == Some(current_id) {
26 return Err(TaskError::InvalidConfiguration);
27 }
28 let current_core = Arc::clone(current.runtime_core_arc());
29 let scheduler_exit = current_core
34 .close_owned_scheduler_activity()
35 .ok_or(TaskError::ThreadBusy)?;
36 let state = self.state.lock();
37 state.ensure_cpu_online(&cpu)?;
38 let record = state.thread_record(current_id)?;
39 if !Arc::ptr_eq(&record.core, ¤t_core) {
40 return Err(TaskError::StaleThreadId);
41 }
42 let sched = record.sched.lock();
43 let placement = record.sched.placement();
44 let lifecycle = sched.lifecycle.state();
45 if lifecycle != ThreadState::Running {
46 return Err(TaskError::InvalidTransition {
47 from: lifecycle,
48 to: ThreadState::Exited,
49 });
50 }
51 if sched.pi.blocked_on.is_some() || !sched.pi.donors.is_empty() {
52 return Err(TaskError::InvalidPiState);
53 }
54 if placement.queued_cpu() != Some(cpu.owner()) || placement.on_cpu() != Some(cpu.owner()) {
55 return Err(TaskError::ThreadBusy);
56 }
57 if require_runtime_context && record.resources.context().is_none() {
58 return Err(TaskError::InvalidRuntimeHandle);
59 }
60 record.callbacks.validate_prepare_exit()?;
61 Ok(CurrentExitPermit {
62 scheduler_exit,
63 current_core,
64 })
65 }
66
67 pub fn exit_current(
72 &self,
73 mut cpu: Pin<&mut CpuLocal>,
74 current: ThreadHandle,
75 ) -> Result<ScheduleDecision, TaskError> {
76 let permit = self.prepare_current_exit_inner(cpu.as_mut(), ¤t, false)?;
80 drop(current);
84 self.commit_current_exit_after_owner_drain(cpu, permit)
85 }
86
87 pub(crate) unsafe fn commit_prepared_current_exit(
94 &self,
95 cpu: Pin<&mut CpuLocal>,
96 permit: CurrentExitPermit,
97 ) -> ScheduleDecision {
98 let exiting = permit.thread();
99 if self.ensure_owner_cpu_context(&cpu).is_err()
100 || cpu.as_ref().get_ref().switch_handoff().is_some()
101 {
102 task_runtime::fatal_invariant(0x4558_0014, exiting.as_u64() as usize);
103 }
104 self.commit_current_exit_owner(cpu, permit, OwnerRqEntry::SchedulerFrame)
105 .unwrap_or_else(|_| {
106 task_runtime::fatal_invariant(0x4558_0015, exiting.as_u64() as usize)
107 })
108 }
109
110 pub(in crate::sched::system::task_system) fn commit_current_exit_after_owner_drain(
117 &self,
118 cpu: Pin<&mut CpuLocal>,
119 permit: CurrentExitPermit,
120 ) -> Result<ScheduleDecision, TaskError> {
121 self.commit_current_exit_owner(cpu, permit, OwnerRqEntry::IrqSave)
122 }
123
124 pub(super) fn commit_current_exit_owner(
125 &self,
126 mut cpu: Pin<&mut CpuLocal>,
127 mut permit: CurrentExitPermit,
128 rq_entry: OwnerRqEntry,
129 ) -> Result<ScheduleDecision, TaskError> {
130 let exiting = permit.thread();
131 let exited_core = Arc::clone(permit.current_core());
132 {
133 let state = self.state.lock();
134 state.ensure_cpu_online(&cpu)?;
135 let record = state.thread_record(exiting)?;
136 if !Arc::ptr_eq(&record.core, &exited_core) {
137 return Err(TaskError::StaleThreadId);
138 }
139 if record.has_live_pi_edges() {
140 return Err(TaskError::InvalidPiState);
141 }
142 record.callbacks.validate_prepare_exit()?;
143 }
144
145 let remote = unsafe { cpu.as_ref().get_ref().remote_for_owner() };
148 let initial_request = remote.claim_scheduler_request(SchedulerRequestScope::All);
149 let mut exited_sched = unsafe { rq_entry.lock_thread_sched(exited_core.sched()) };
151 let mut transaction = unsafe { rq_entry.begin(self, remote) };
153 let now_ns = transaction.clock().wall().as_nanos();
154 if transaction.current_thread() != Some(exiting)
155 || transaction
156 .current_core()
157 .is_none_or(|core| !Arc::ptr_eq(&core, &exited_core))
158 {
159 transaction.adopt_scheduler_request(initial_request);
160 transaction.commit_and_finish_scheduler_request();
161 return Err(TaskError::StaleThreadId);
162 }
163 transaction.adopt_scheduler_request(initial_request);
164 transaction.merge_scheduler_request(SchedulerRequestScope::All);
165 let dispatch_commit = self.settle_owner_current_dispatch_in_rq(&mut transaction);
166 transaction.merge_scheduler_request(SchedulerRequestScope::All);
169 let previous_endpoint = transaction.current_switch_endpoint().unwrap_or_else(|| {
170 task_runtime::fatal_invariant(0x4558_0007, exiting.as_u64() as usize)
171 });
172 let previous_urgency = transaction.current_scheduling_urgency().unwrap_or_else(|| {
173 task_runtime::fatal_invariant(0x4558_0007, exiting.as_u64() as usize)
174 });
175 let held_reservation = {
176 let placement = exited_core.sched().placement();
177 let sched = &mut *exited_sched;
178 if sched.lifecycle.state() != ThreadState::Running
179 || placement.queued_cpu() != Some(cpu.owner())
180 || placement.on_cpu() != Some(cpu.owner())
181 {
182 task_runtime::fatal_invariant(0x4558_1101, exiting.as_u64() as usize);
183 }
184 Self::detach_owner_deadline_bandwidth_in_rq(
185 &exited_core,
186 sched,
187 cpu.remote(),
188 &mut transaction,
189 );
190 if transaction.is_linked_current(exiting) {
191 transaction.deactivate_task(exiting);
192 } else {
193 transaction.deactivate_unlinked_current(exiting);
194 }
195 if sched.transition(&exited_core, ThreadState::Exited).is_err() {
196 task_runtime::fatal_invariant(0x4558_0001, exiting.as_u64() as usize);
197 }
198 placement.block_current(cpu.owner());
203 permit.seal();
204 let held = sched.held_deadline_reservation();
205 sched.deadline.bandwidth.replace_detached_reservation(0);
206 sched.policy.discard_pending_update();
207 held
208 };
209 transaction.take_current();
210 let next = self.pick_owner_next_in_rq(cpu.as_mut(), &mut transaction, None);
211 let OwnerNext {
212 core: next_core,
213 policy: next_policy_ref,
214 urgency: next_urgency,
215 } = next;
216 let next_endpoint = transaction.current_switch_endpoint().unwrap_or_else(|| {
217 task_runtime::fatal_invariant(0x4558_0008, next_core.as_ref().id().as_u64() as usize)
218 });
219 let handoff = Self::prepare_switch_handoff(
220 Some(exiting),
221 Some(PreviousSwitchOwnership::retained(Arc::clone(&exited_core))),
222 next_core,
223 next_policy_ref,
224 PreviousSwitchDisposition::Exited,
225 None,
226 );
227 let deadline_rq_observation =
228 transaction.scheduler_deadline_rq_observation(cpu.as_ref().get_ref());
229 self.commit_owner_switch_selection(cpu.as_mut(), transaction, handoff, false);
230 drop(exited_sched);
231 self.finish_owner_dispatch_commit(dispatch_commit);
232
233 {
234 let mut state = self.state.lock();
235 let record = state.thread_record_mut(exiting).unwrap_or_else(|_| {
236 task_runtime::fatal_invariant(0x4558_0002, exiting.as_u64() as usize)
237 });
238 if record
239 .callbacks
240 .prepare_exit(record.extension.is_some())
241 .is_err()
242 {
243 task_runtime::fatal_invariant(0x4558_0003, exiting.as_u64() as usize);
244 }
245 state.queue_exited_thread(exiting);
246 }
247 self.root_domain.lock().release_deadline(held_reservation);
248 exited_core.notify_affinity_waiters();
249 drop(permit);
250 self.finish_owner_selection(
251 cpu.as_mut(),
252 Some(previous_endpoint.thread()),
253 next_endpoint.thread(),
254 Some(previous_urgency),
255 next_urgency,
256 OwnerSchedulerDeadline::Reevaluate(deadline_rq_observation),
257 );
258 let decision = Self::owner_switch_plan(
259 Some(previous_endpoint),
260 next_endpoint,
261 SwitchReason::Exited,
262 now_ns,
263 );
264 Ok(decision)
265 }
266}