ax_task/sched/system/task_system/
thread_creation.rs1use super::*;
4
5struct ThreadSlotReservation<'system> {
7 system: &'system TaskSystem,
8 slot: u32,
9 generation: u32,
10 bandwidth: u64,
11 committed: bool,
12}
13
14impl Drop for ThreadSlotReservation<'_> {
15 fn drop(&mut self) {
16 if self.committed {
17 return;
18 }
19 let mut state = self.system.state.lock();
20 let mut root_domain = self.system.root_domain.lock();
21 let slot = &mut state.slots[self.slot as usize];
22 assert_eq!(slot.generation, self.generation);
23 assert!(slot.record.is_none());
24 assert_eq!(slot.pending_deadline_reservation, self.bandwidth);
25 slot.pending_deadline_reservation = 0;
26 if advance_thread_slot_generation(slot) {
27 state.free_slots.push(self.slot);
28 }
29 root_domain.release_deadline(self.bandwidth);
30 }
31}
32
33impl TaskSystem {
34 pub fn create_thread(&self, spec: ThreadSpec) -> Result<ThreadHandle, TaskError> {
39 let initial_cpu = CpuId::new(unsafe { task_runtime::current_cpu_id() }.as_u32());
43 self.create_thread_on_cpu(spec, initial_cpu)
44 }
45
46 fn create_thread_on_cpu(
52 &self,
53 spec: ThreadSpec,
54 initial_cpu: CpuId,
55 ) -> Result<ThreadHandle, TaskError> {
56 use crate::thread::allocation::try_arc;
57 let mut unpublished = UnpublishedThreadGuard::new(self, spec);
59 if initial_cpu.as_usize() >= self.config.cpu_count() {
60 return Err(TaskError::InvalidCpu(initial_cpu.as_u32()));
61 }
62 let policy = unpublished.spec().policy();
63 policy.validate()?;
64 let spec = unpublished
65 .spec
66 .as_mut()
67 .expect("unpublished specification");
68 let affinity = match spec.take_affinity() {
69 Some(affinity) => affinity,
70 None => CpuSet::try_all(self.config.cpu_count())?,
71 };
72 validate_affinity(&affinity, self.config.cpu_count())?;
73 let affinity = try_arc(affinity)?;
74 let execution = spec.execution.take();
75 let mut reservation = {
76 let mut state = self.state.lock();
77 let mut root_domain = self.root_domain.lock();
78 let bandwidth = root_domain.reserve_deadline(policy, &affinity)?;
79 let (slot, generation) = match state.allocate_thread_slot(self.config.thread_capacity())
80 {
81 Ok(identity) => identity,
82 Err(error) => {
83 root_domain.release_deadline(bandwidth);
84 return Err(error);
85 }
86 };
87 state.slots[slot as usize].pending_deadline_reservation = bandwidth;
88 ThreadSlotReservation {
89 system: self,
90 slot,
91 generation,
92 bandwidth,
93 committed: false,
94 }
95 };
96 let id = ThreadId::from_parts(reservation.slot, reservation.generation);
97 let deadline_server = DeadlineServer::unbound()?;
100 let entity = SchedulingEntity::new_with_deadline_server(
101 policy,
102 self.config.fair_slice_ns(),
103 0,
104 deadline_server.clone(),
105 );
106 let extension = unpublished.spec().extension();
107 let switch_extension = extension.map(ThreadExtension::as_view);
108 let scheduler_tick_cpu_time = extension.and_then(ThreadExtension::scheduler_tick_cpu_time);
109 let scheduler_tick_work = extension.and_then(ThreadExtension::scheduler_tick_work);
110 let resources = unpublished.spec().resources();
111 let address_space = resources.address_space();
112 let membarrier_identity = if address_space.is_none() {
113 crate::runtime::resource::AddressSpaceMembarrierId::NONE
114 } else {
115 task_runtime::address_space_membarrier_state(address_space).identity()
116 };
117 let sched = try_arc(ThreadSchedCell::new(
118 id,
119 ThreadSchedInit {
120 policy: ThreadPolicyInit { policy, entity },
121 placement: ThreadPlacementInit {
122 initial_cpu,
123 affinity: Arc::clone(&affinity),
124 },
125 deadline: ThreadDeadlineInit {
126 server: deadline_server,
127 reservation_scaled: reservation.bandwidth,
128 },
129 runtime: ThreadRuntimeInit {
130 context: resources.context(),
131 address_space,
132 },
133 },
134 )?)?;
135 let core = try_arc(ThreadCore::new(ThreadCoreInit {
136 id,
137 policy,
138 sched: Arc::clone(&sched),
139 extension: switch_extension,
140 execution,
141 scheduler_tick_cpu_time,
142 scheduler_tick_work,
143 membarrier_identity,
144 task_work: Some(Arc::clone(&self.task_work)),
145 })?)?;
146 let (extension, resources) = unpublished.into_owned_parts();
147 let record = ThreadRecord {
148 core: Arc::clone(&core),
149 sched,
150 resources,
151 extension,
152 callbacks: ThreadCallbackState::new(),
153 activation: None,
154 };
155 let context = record.resources.context();
156 if !context.is_none() {
157 let status = task_runtime::bind_context_thread(ContextThreadBinding {
158 context,
159 publication: CurrentThreadPublication::from_core(id, &core),
160 });
161 if status != RuntimeStatus::Success {
162 drop(reservation);
163 drop(core);
164 self.release_thread_record(record);
165 return Err(TaskError::RuntimeFailure(status as u32));
166 }
167 }
168 let mut record = Some(record);
169 let commit_error = {
170 let mut state = self.state.lock();
171 let root_domain = self.root_domain.lock();
172 let is_deadline = matches!(policy, SchedulePolicy::Deadline(_));
173 if is_deadline && !affinity.covers(&root_domain.online) {
174 Some(TaskError::DeadlineAffinity)
175 } else if is_deadline && root_domain.admission_overcommitted() {
176 Some(TaskError::DeadlineAdmission)
177 } else {
178 let slot = &mut state.slots[reservation.slot as usize];
179 assert_eq!(slot.generation, reservation.generation);
180 assert!(slot.record.is_none());
181 assert_eq!(slot.pending_deadline_reservation, reservation.bandwidth);
182 slot.pending_deadline_reservation = 0;
183 slot.record = record.take();
184 reservation.committed = true;
185 None
186 }
187 };
188 if let Some(error) = commit_error {
189 drop(reservation);
190 drop(core);
191 self.release_thread_record(record.expect("rejected commit owns its record"));
192 return Err(error);
193 }
194 Ok(ThreadHandle::from_core(core))
195 }
196
197 unsafe fn make_ready_bootstrap(&self, thread: ThreadId) -> Result<(), TaskError> {
204 let state = self.state.lock();
205 let record = state.thread_record(thread)?;
206 let mut sched = unsafe { record.sched.lock_bootstrap() };
208 sched.transition(&record.core, ThreadState::Running)
209 }
210
211 pub fn install_bootstrap_thread(
217 &self,
218 mut cpu: Pin<&mut CpuLocal>,
219 spec: ThreadSpec,
220 ) -> Result<ThreadHandle, TaskError> {
221 let unpublished = UnpublishedThreadGuard::new(self, spec);
222 self.ensure_owner_cpu_context(&cpu)?;
223 if !matches!(
224 unpublished.spec().policy(),
225 SchedulePolicy::Fair {
226 mode: FairMode::Normal | FairMode::Batch,
227 ..
228 }
229 ) {
230 return Err(TaskError::InvalidConfiguration);
231 }
232 {
233 let state = self.state.lock();
234 let registration = state.cpu_registration(cpu.owner())?;
235 if !Arc::ptr_eq(®istration.remote, cpu.remote()) {
236 return Err(TaskError::InvalidRuntimeHandle);
237 }
238 if unsafe { cpu.remote().lock_run_queue_irq_disabled() }
241 .current_thread()
242 .is_some()
243 {
244 return Err(TaskError::InvalidConfiguration);
245 }
246 }
247
248 let thread = self.create_thread_on_cpu(unpublished.into_spec(), cpu.owner())?;
249 let setup = (|| {
250 let core = {
251 let state = self.state.lock();
252 Arc::clone(&state.thread_record(thread.id())?.core)
253 };
254 let mut sched = unsafe { core.sched().lock_bootstrap() };
257 sched.transition(&core, ThreadState::Running)?;
258 let remote = Arc::clone(cpu.remote());
259 let mut transaction = unsafe { OwnerRqTxn::begin_bootstrap(self, &remote) };
262 let _enqueue_consumed_by_immediate_bootstrap_pick = self
263 .link_owner_ready_thread_locked(
264 cpu.owner(),
265 &mut transaction,
266 &core,
267 &mut sched,
268 EnqueueReason::Wake,
269 );
270 let next = self.pick_owner_bootstrap_in_rq(cpu.as_mut(), &mut transaction);
271 if !core::ptr::eq(next.core.as_ref(), Arc::as_ref(&core)) {
272 task_runtime::fatal_invariant(0x4254_0001, core.id().as_u64() as usize);
273 }
274 transaction.commit_bootstrap();
275 Ok(())
276 })();
277 if let Err(error) = setup {
278 return match self.discard_unpublished_thread(thread) {
279 Ok(()) => Err(error),
280 Err(cleanup_error) => Err(cleanup_error),
281 };
282 }
283 Ok(thread)
284 }
285
286 pub fn register_idle_thread(
288 &self,
289 mut cpu: Pin<&mut CpuLocal>,
290 spec: ThreadSpec,
291 ) -> Result<ThreadHandle, TaskError> {
292 let unpublished = UnpublishedThreadGuard::new(self, spec);
293 self.ensure_owner_cpu_context(&cpu)?;
294 if !matches!(
295 unpublished.spec().policy(),
296 SchedulePolicy::Fair {
297 mode: crate::sched::FairMode::Idle,
298 ..
299 }
300 ) {
301 return Err(TaskError::InvalidConfiguration);
302 }
303 {
304 let state = self.state.lock();
305 let registration = state.cpu_registration(cpu.owner())?;
306 if !Arc::ptr_eq(®istration.remote, cpu.remote()) {
307 return Err(TaskError::InvalidRuntimeHandle);
308 }
309 if unsafe { cpu.remote().lock_run_queue_irq_disabled() }
312 .idle()
313 .is_some()
314 {
315 return Err(TaskError::InvalidConfiguration);
316 }
317 }
318
319 let thread = self.create_thread_on_cpu(unpublished.into_spec(), cpu.owner())?;
320 let setup = unsafe { self.make_ready_bootstrap(thread.id()) }.and_then(|()| {
323 let state = self.state.lock();
324 let core = Arc::clone(&state.thread_record(thread.id())?.core);
325 drop(state);
326 self.install_idle_core(cpu.as_mut(), core)
327 });
328 if let Err(error) = setup {
329 return match self.discard_unpublished_thread(thread) {
330 Ok(()) => Err(error),
331 Err(cleanup_error) => Err(cleanup_error),
332 };
333 }
334 Ok(thread)
335 }
336
337 pub(super) fn install_idle_core(
341 &self,
342 mut cpu: Pin<&mut CpuLocal>,
343 core: Arc<ThreadCore>,
344 ) -> Result<(), TaskError> {
345 let owner = cpu.owner();
346 if unsafe { cpu.remote().lock_run_queue_irq_disabled() }
349 .idle()
350 .is_some()
351 {
352 return Err(TaskError::InvalidConfiguration);
353 }
354 let mut sched = unsafe { core.sched().lock_bootstrap() };
357 let policy = core.sched().active(&sched).policy();
358 if sched.lifecycle.state() != ThreadState::Running
359 || !matches!(
360 policy,
361 SchedulePolicy::Fair {
362 mode: crate::sched::FairMode::Idle,
363 ..
364 }
365 )
366 || !sched.affinity.affinity.contains(owner)
367 || sched.placement.assigned_cpu() != Some(owner)
368 || sched.placement.on_cpu().is_some()
369 || sched.placement.requested_migration().is_some()
370 {
371 return Err(TaskError::InvalidConfiguration);
372 }
373 let metadata = sched.rq_task_metadata()?;
374 let rt_quota_exempt = sched.is_pi_boosted_rt_owner_for(policy);
375 let active = core.sched().take_active(&mut sched);
376 unsafe {
379 cpu.as_mut().install_idle_bootstrap(
380 self,
381 core.id(),
382 Arc::clone(&core),
383 active,
384 metadata,
385 rt_quota_exempt,
386 )
387 };
388 core.set_wake_cpu_hint(owner);
389 Ok(())
390 }
391
392 fn discard_unpublished_thread(&self, handle: ThreadHandle) -> Result<(), TaskError> {
393 let record = {
394 let mut state = self.state.lock();
395 let mut root_domain = self.root_domain.lock();
396 let (record, released) = state.remove_unpublished_thread_with_handle(&handle)?;
397 root_domain.release_deadline(released);
398 record
399 };
400 drop(handle);
401 self.release_thread_record(record);
402 Ok(())
403 }
404}