1use alloc::{sync::Arc, vec, vec::Vec};
4
5use crate::{
6 runtime::{
7 resource::{
8 AddressSpaceHandle, AddressSpaceToken, ExecutionContextHandle, StackHandle, TlsHandle,
9 },
10 service::{SchedulerTickCpuTime, SchedulerTickGate, SchedulerTickTaskWork},
11 task_runtime,
12 },
13 sched::{CpuId, SchedulePolicy},
14 thread::{SchedulerTickWork, TaskError, ThreadHandle, ThreadId},
15};
16
17#[repr(C)]
19#[derive(Debug, Eq, PartialEq)]
20pub struct ThreadResources {
21 context: ExecutionContextHandle,
22 stack: StackHandle,
23 tls: TlsHandle,
24 address_space: AddressSpaceToken,
25}
26
27impl ThreadResources {
28 pub const NONE: Self = Self {
30 context: ExecutionContextHandle::NONE,
31 stack: StackHandle::NONE,
32 tls: TlsHandle::NONE,
33 address_space: AddressSpaceToken::NONE,
34 };
35
36 pub const unsafe fn new(
45 context: ExecutionContextHandle,
46 stack: StackHandle,
47 tls: TlsHandle,
48 address_space: AddressSpaceToken,
49 ) -> Self {
50 Self {
51 context,
52 stack,
53 tls,
54 address_space,
55 }
56 }
57
58 pub const fn context(&self) -> ExecutionContextHandle {
60 self.context
61 }
62 pub const fn stack(&self) -> StackHandle {
64 self.stack
65 }
66 pub const fn tls(&self) -> TlsHandle {
68 self.tls
69 }
70 pub const fn address_space(&self) -> AddressSpaceHandle {
72 self.address_space.handle()
73 }
74
75 pub(crate) fn replace_address_space(
76 &mut self,
77 address_space: AddressSpaceToken,
78 ) -> AddressSpaceToken {
79 core::mem::replace(&mut self.address_space, address_space)
80 }
81
82 pub(crate) fn take_address_space(&mut self) -> AddressSpaceToken {
83 core::mem::replace(&mut self.address_space, AddressSpaceToken::NONE)
84 }
85
86 pub(crate) fn release(mut self) -> AddressSpaceToken {
95 if !self.context.is_none() {
96 task_runtime::destroy_context(self.context);
97 self.context = ExecutionContextHandle::NONE;
98 }
99
100 if !self.tls.is_none() {
101 task_runtime::deallocate_tls(self.tls);
102 self.tls = TlsHandle::NONE;
103 }
104
105 if !self.stack.is_none() {
106 task_runtime::deallocate_stack(self.stack);
107 self.stack = StackHandle::NONE;
108 }
109
110 core::mem::replace(&mut self.address_space, AddressSpaceToken::NONE)
111 }
112}
113
114#[repr(u32)]
119#[derive(Clone, Copy, Debug, Eq, PartialEq)]
120pub enum SwitchReason {
121 Preempted = 1,
123 Yield = 2,
125 Blocked = 3,
127 Exited = 4,
129 Migrated = 5,
131}
132
133#[derive(Clone, Debug, Eq, PartialEq)]
135pub struct CpuSet {
136 words: Vec<usize>,
137 topology_len: usize,
138 allowed_count: usize,
141}
142
143impl CpuSet {
144 const BITS_PER_WORD: usize = usize::BITS as usize;
145
146 pub fn all(cpu_count: usize) -> Self {
148 let mut words = vec![usize::MAX; cpu_count.div_ceil(Self::BITS_PER_WORD)];
149 if let Some(last) = words.last_mut()
150 && !cpu_count.is_multiple_of(Self::BITS_PER_WORD)
151 {
152 *last = (1usize << (cpu_count % Self::BITS_PER_WORD)) - 1;
153 }
154 Self {
155 words,
156 topology_len: cpu_count,
157 allowed_count: cpu_count,
158 }
159 }
160
161 pub(crate) fn try_all(cpu_count: usize) -> Result<Self, super::TaskError> {
162 let mut words =
163 crate::thread::allocation::try_vec(cpu_count.div_ceil(Self::BITS_PER_WORD))?;
164 words.resize(cpu_count.div_ceil(Self::BITS_PER_WORD), usize::MAX);
165 if let Some(last) = words.last_mut()
166 && !cpu_count.is_multiple_of(Self::BITS_PER_WORD)
167 {
168 *last = (1usize << (cpu_count % Self::BITS_PER_WORD)) - 1;
169 }
170 Ok(Self {
171 words,
172 topology_len: cpu_count,
173 allowed_count: cpu_count,
174 })
175 }
176
177 pub fn empty(cpu_count: usize) -> Self {
179 Self {
180 words: vec![0; cpu_count.div_ceil(Self::BITS_PER_WORD)],
181 topology_len: cpu_count,
182 allowed_count: 0,
183 }
184 }
185
186 pub fn insert(&mut self, cpu: CpuId) -> bool {
188 let index = cpu.as_usize();
189 if index >= self.topology_len {
190 return false;
191 }
192 let mask = 1usize << (index % Self::BITS_PER_WORD);
193 let word = &mut self.words[index / Self::BITS_PER_WORD];
194 let changed = *word & mask == 0;
195 *word |= mask;
196 if changed {
197 self.allowed_count += 1;
198 }
199 changed
200 }
201
202 pub fn remove(&mut self, cpu: CpuId) -> bool {
204 let index = cpu.as_usize();
205 if index >= self.topology_len {
206 return false;
207 }
208 let mask = 1usize << (index % Self::BITS_PER_WORD);
209 let word = &mut self.words[index / Self::BITS_PER_WORD];
210 let changed = *word & mask != 0;
211 *word &= !mask;
212 if changed {
213 self.allowed_count -= 1;
214 }
215 changed
216 }
217
218 pub(crate) fn clear(&mut self) {
219 self.words.fill(0);
220 self.allowed_count = 0;
221 }
222
223 pub fn contains(&self, cpu: CpuId) -> bool {
225 let index = cpu.as_usize();
226 index < self.topology_len
227 && self.words[index / Self::BITS_PER_WORD] & (1usize << (index % Self::BITS_PER_WORD))
228 != 0
229 }
230
231 pub fn topology_len(&self) -> usize {
233 self.topology_len
234 }
235
236 pub(crate) fn count(&self) -> usize {
238 self.allowed_count
239 }
240
241 pub fn iter(&self) -> impl Iterator<Item = CpuId> + '_ {
243 (0..self.topology_len)
244 .map(|index| CpuId::new(index as u32))
245 .filter(|cpu| self.contains(*cpu))
246 }
247
248 pub(crate) fn sole_cpu(&self) -> Option<CpuId> {
250 if self.allowed_count != 1 {
251 return None;
252 }
253 let (word_index, word) = self
254 .words
255 .iter()
256 .copied()
257 .enumerate()
258 .find(|(_, word)| *word != 0)?;
259 let index = word_index * Self::BITS_PER_WORD + word.trailing_zeros() as usize;
260 (index < self.topology_len).then_some(CpuId::new(index as u32))
261 }
262
263 pub(crate) fn is_migration_capable(&self) -> bool {
265 self.allowed_count > 1
266 }
267
268 pub fn covers(&self, required: &Self) -> bool {
270 self.topology_len == required.topology_len
271 && self
272 .words
273 .iter()
274 .zip(&required.words)
275 .all(|(allowed, is_required)| allowed & is_required == *is_required)
276 }
277
278 pub(crate) fn copy_from_set(&mut self, source: &Self) -> Result<(), TaskError> {
279 if self.topology_len != source.topology_len {
280 return Err(TaskError::InvalidConfiguration);
281 }
282 self.words.copy_from_slice(&source.words);
283 self.allowed_count = source.allowed_count;
284 Ok(())
285 }
286
287 pub(crate) fn first_intersection(
293 &self,
294 other: &Self,
295 mut accepts: impl FnMut(CpuId) -> bool,
296 ) -> Option<CpuId> {
297 if self.topology_len != other.topology_len {
298 return None;
299 }
300 for (word_index, (left, right)) in self.words.iter().zip(&other.words).enumerate() {
301 let mut candidates = left & right;
302 while candidates != 0 {
303 let bit = candidates.trailing_zeros() as usize;
304 candidates &= candidates - 1;
305 let index = word_index * Self::BITS_PER_WORD + bit;
306 if index >= self.topology_len {
307 break;
308 }
309 let cpu = CpuId::new(index as u32);
310 if accepts(cpu) {
311 return Some(cpu);
312 }
313 }
314 }
315 None
316 }
317
318 pub(crate) fn word(&self, word_index: usize) -> usize {
319 self.words.get(word_index).copied().unwrap_or(0)
320 }
321}
322
323#[repr(C)]
325#[derive(Debug)]
326pub struct ThreadExtensionOps {
327 pub on_switch_in: unsafe extern "Rust" fn(
331 data: usize,
332 thread: ThreadId,
333 policy: SchedulePolicy,
334 charged_runtime_ns: u64,
335 ),
336 pub on_switch_out: unsafe extern "Rust" fn(data: usize, thread: ThreadId, reason: SwitchReason),
338 pub on_exit: unsafe extern "Rust" fn(data: usize, thread: ThreadId),
340 pub on_deadline_overrun: unsafe extern "Rust" fn(data: usize, thread: ThreadId),
342 pub drop: unsafe extern "Rust" fn(data: usize),
344}
345
346pub type RunningPolicyAppliedHook = unsafe extern "Rust" fn(
348 data: usize,
349 thread: ThreadId,
350 base_policy: SchedulePolicy,
351 observed_ns: u64,
352);
353
354#[derive(Debug)]
356pub struct ThreadExtension {
357 data: usize,
358 ops: &'static ThreadExtensionOps,
359 running_policy_applied_hook: Option<RunningPolicyAppliedHook>,
360 scheduler_tick_cpu_time: Option<Arc<SchedulerTickCpuTime>>,
361 scheduler_tick_work: Option<SchedulerTickWork>,
362}
363
364impl ThreadExtension {
365 pub const unsafe fn new(data: usize, ops: &'static ThreadExtensionOps) -> Self {
375 Self {
376 data,
377 ops,
378 running_policy_applied_hook: None,
379 scheduler_tick_cpu_time: None,
380 scheduler_tick_work: None,
381 }
382 }
383
384 pub fn with_scheduler_tick_cpu_time(mut self, accounting: Arc<SchedulerTickCpuTime>) -> Self {
389 self.scheduler_tick_cpu_time = Some(accounting);
390 self
391 }
392
393 pub unsafe fn with_running_policy_applied_hook(
407 mut self,
408 callback: RunningPolicyAppliedHook,
409 ) -> Self {
410 self.running_policy_applied_hook = Some(callback);
411 self
412 }
413
414 pub unsafe fn with_scheduler_tick_work(
428 mut self,
429 gate: Arc<SchedulerTickGate>,
430 callback: SchedulerTickTaskWork,
431 ) -> Self {
432 self.scheduler_tick_work = Some(SchedulerTickWork::new(gate, callback));
433 self
434 }
435
436 pub const fn data(&self) -> usize {
438 self.data
439 }
440
441 pub const fn ops(&self) -> &'static ThreadExtensionOps {
443 self.ops
444 }
445
446 pub fn scheduler_tick_cpu_time(&self) -> Option<Arc<SchedulerTickCpuTime>> {
450 self.scheduler_tick_cpu_time.as_ref().map(Arc::clone)
451 }
452
453 pub(crate) const fn as_view(&self) -> ThreadExtensionView {
454 ThreadExtensionView {
455 data: self.data,
456 ops: self.ops,
457 running_policy_applied_hook: self.running_policy_applied_hook,
458 }
459 }
460
461 pub(crate) fn scheduler_tick_work(&self) -> Option<SchedulerTickWork> {
462 self.scheduler_tick_work.clone()
463 }
464}
465
466impl Drop for ThreadExtension {
467 fn drop(&mut self) {
468 unsafe { (self.ops.drop)(self.data) };
471 }
472}
473
474#[derive(Clone, Copy, Debug)]
476pub struct ThreadExtensionView {
477 data: usize,
478 ops: &'static ThreadExtensionOps,
479 running_policy_applied_hook: Option<RunningPolicyAppliedHook>,
480}
481
482#[derive(Debug)]
488pub struct ThreadExtensionBorrow<'thread> {
489 view: ThreadExtensionView,
490 _thread: &'thread ThreadHandle,
491}
492
493impl<'thread> ThreadExtensionBorrow<'thread> {
494 pub(crate) const fn new(view: ThreadExtensionView, thread: &'thread ThreadHandle) -> Self {
495 Self {
496 view,
497 _thread: thread,
498 }
499 }
500
501 pub const fn data(&self) -> usize {
503 self.view.data()
504 }
505
506 pub const fn ops(&self) -> &'static ThreadExtensionOps {
508 self.view.ops()
509 }
510}
511
512#[derive(Debug)]
518pub struct ThreadExtensionLease {
519 view: ThreadExtensionView,
520 thread: ThreadHandle,
521}
522
523impl ThreadExtensionLease {
524 pub(crate) const fn new(view: ThreadExtensionView, thread: ThreadHandle) -> Self {
525 Self { view, thread }
526 }
527
528 pub fn thread_id(&self) -> ThreadId {
530 self.thread.id()
531 }
532
533 pub const fn data(&self) -> usize {
535 self.view.data()
536 }
537
538 pub const fn ops(&self) -> &'static ThreadExtensionOps {
540 self.view.ops()
541 }
542}
543
544impl ThreadExtensionView {
545 pub const fn data(self) -> usize {
547 self.data
548 }
549
550 pub const fn ops(self) -> &'static ThreadExtensionOps {
552 self.ops
553 }
554
555 pub(crate) unsafe fn notify_running_policy_applied(
556 self,
557 thread: ThreadId,
558 base_policy: SchedulePolicy,
559 observed_ns: u64,
560 ) {
561 if let Some(callback) = self.running_policy_applied_hook {
562 unsafe { callback(self.data, thread, base_policy, observed_ns) };
563 }
564 }
565}
566
567#[derive(Debug)]
569pub struct ThreadSpec {
570 pub(crate) execution: Option<Arc<crate::thread::execution::ThreadExecution>>,
571 policy: SchedulePolicy,
572 affinity: Option<CpuSet>,
573 resources: ThreadResources,
576 extension: Option<ThreadExtension>,
577}
578
579impl ThreadSpec {
580 pub const fn new(policy: SchedulePolicy) -> Self {
582 Self {
583 execution: None,
584 policy,
585 affinity: None,
586 resources: ThreadResources::NONE,
587 extension: None,
588 }
589 }
590
591 pub fn with_affinity(mut self, affinity: CpuSet) -> Self {
593 self.affinity = Some(affinity);
594 self
595 }
596
597 pub fn with_extension(mut self, extension: ThreadExtension) -> Self {
599 self.extension = Some(extension);
600 self
601 }
602
603 pub unsafe fn with_resources(mut self, resources: ThreadResources) -> Self {
610 self.resources = resources;
611 self
612 }
613
614 pub const fn policy(&self) -> SchedulePolicy {
616 self.policy
617 }
618
619 pub fn affinity(&self) -> Option<&CpuSet> {
621 self.affinity.as_ref()
622 }
623
624 pub(crate) fn take_affinity(&mut self) -> Option<CpuSet> {
625 self.affinity.take()
626 }
627 pub(crate) fn resources(&self) -> &ThreadResources {
628 &self.resources
629 }
630 pub(crate) fn extension(&self) -> Option<&ThreadExtension> {
631 self.extension.as_ref()
632 }
633
634 pub(crate) fn into_owned_parts(mut self) -> (Option<ThreadExtension>, ThreadResources) {
635 let extension = self.extension.take();
636 let resources = core::mem::replace(&mut self.resources, ThreadResources::NONE);
637 (extension, resources)
638 }
639}