1#![allow(clippy::type_complexity)]
2
3#[cfg(not(feature = "common-os"))]
4pub(crate) mod tls;
5
6use alloc::collections::{LinkedList, VecDeque};
7use alloc::rc::Rc;
8use alloc::sync::Arc;
9use core::cell::RefCell;
10use core::num::NonZeroU64;
11use core::{cmp, fmt};
12
13use ahash::RandomState;
14use crossbeam_utils::CachePadded;
15use hashbrown::HashMap;
16use hermit_sync::{OnceCell, RwSpinLock};
17use memory_addresses::VirtAddr;
18
19#[cfg(not(feature = "common-os"))]
20use self::tls::Tls;
21use super::timer_interrupts::{Source, create_timer_abs};
22use crate::arch;
23use crate::arch::core_local::*;
24use crate::arch::scheduler::TaskStacks;
25use crate::fd::{Fd, RawFd, stdio};
26use crate::scheduler::CoreId;
27
28#[inline]
38fn msb(n: u64) -> Option<u32> {
39 NonZeroU64::new(n).map(|n| u64::BITS - 1 - n.leading_zeros())
40}
41
42#[derive(Copy, Clone, Debug, Eq, PartialEq)]
44pub(crate) enum TaskStatus {
45 Invalid,
46 Ready,
47 Running,
48 Blocked,
49 Finished,
50 Idle,
51}
52
53#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
55pub struct TaskId(i32);
56
57impl TaskId {
58 pub const fn into(self) -> i32 {
59 self.0
60 }
61
62 pub const fn from(x: i32) -> Self {
63 TaskId(x)
64 }
65}
66
67impl fmt::Display for TaskId {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
75pub struct Priority(u8);
76
77impl Priority {
78 pub const fn into(self) -> u8 {
79 self.0
80 }
81
82 pub const fn from(x: u8) -> Self {
83 Priority(x)
84 }
85}
86
87impl fmt::Display for Priority {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{}", self.0)
90 }
91}
92
93#[allow(dead_code)]
94pub const HIGH_PRIO: Priority = Priority::from(3);
95pub const NORMAL_PRIO: Priority = Priority::from(2);
96#[allow(dead_code)]
97pub const LOW_PRIO: Priority = Priority::from(1);
98pub const IDLE_PRIO: Priority = Priority::from(0);
99
100pub const NO_PRIORITIES: usize = 31;
102
103#[derive(Copy, Clone, Debug)]
104pub(crate) struct TaskHandle {
105 id: TaskId,
106 priority: Priority,
107 #[cfg(feature = "smp")]
108 core_id: CoreId,
109}
110
111impl TaskHandle {
112 pub fn new(id: TaskId, priority: Priority, #[cfg(feature = "smp")] core_id: CoreId) -> Self {
113 Self {
114 id,
115 priority,
116 #[cfg(feature = "smp")]
117 core_id,
118 }
119 }
120
121 #[cfg(feature = "smp")]
122 pub fn get_core_id(&self) -> CoreId {
123 self.core_id
124 }
125
126 pub fn get_id(&self) -> TaskId {
127 self.id
128 }
129
130 pub fn get_priority(&self) -> Priority {
131 self.priority
132 }
133}
134
135impl Ord for TaskHandle {
136 fn cmp(&self, other: &Self) -> cmp::Ordering {
137 self.id.cmp(&other.id)
138 }
139}
140
141impl PartialOrd for TaskHandle {
142 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
143 Some(self.cmp(other))
144 }
145}
146
147impl PartialEq for TaskHandle {
148 fn eq(&self, other: &Self) -> bool {
149 self.id == other.id
150 }
151}
152
153impl Eq for TaskHandle {}
154
155#[derive(Default)]
157pub(crate) struct TaskHandlePriorityQueue {
158 queues: [Option<VecDeque<TaskHandle>>; NO_PRIORITIES],
159 prio_bitmap: CachePadded<u64>,
160}
161
162impl TaskHandlePriorityQueue {
163 pub const fn new() -> Self {
165 Self {
166 queues: [const { None }; NO_PRIORITIES],
167 prio_bitmap: CachePadded::new(0),
168 }
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.prio_bitmap.into_inner() == 0
174 }
175
176 pub fn contains(&self, task: TaskHandle) -> bool {
179 matches!(self.queues[task.priority.into() as usize]
180 .as_ref(), Some(queue) if queue.iter().any(|queued| queued.id == task.id))
181 }
182
183 pub fn push(&mut self, task: TaskHandle) {
185 let i = task.priority.into() as usize;
186 *self.prio_bitmap |= (1 << i) as u64;
189 if let Some(queue) = &mut self.queues[i] {
190 queue.push_back(task);
191 } else {
192 let mut queue = VecDeque::new();
193 queue.push_back(task);
194 self.queues[i] = Some(queue);
195 }
196 }
197
198 fn pop_from_queue(&mut self, queue_index: usize) -> Option<TaskHandle> {
199 let queue = self.queues[queue_index].as_mut()?;
200
201 let task = queue.pop_front();
202
203 if queue.is_empty() {
204 *self.prio_bitmap &= !(1 << queue_index as u64);
205 }
206
207 task
208 }
209
210 pub fn pop(&mut self) -> Option<TaskHandle> {
212 let i = msb(self.prio_bitmap.into_inner())?;
213
214 self.pop_from_queue(i as usize)
215 }
216
217 pub fn remove(&mut self, task: TaskHandle) -> bool {
220 let queue_index = task.priority.into() as usize;
221 let mut success = false;
224 if let Some(queue) = &mut self.queues[queue_index] {
225 let mut i = 0;
226 while i != queue.len() {
227 if queue[i].id == task.id {
228 queue.remove(i);
229 success = true;
230 } else {
231 i += 1;
232 }
233 }
234
235 if queue.is_empty() {
236 *self.prio_bitmap &= !(1 << queue_index as u64);
237 }
238 }
239
240 success
241 }
242}
243
244pub(crate) struct PriorityTaskQueue {
246 queues: [LinkedList<Rc<RefCell<Task>>>; NO_PRIORITIES],
247 prio_bitmap: u64,
248}
249
250impl PriorityTaskQueue {
251 pub const fn new() -> PriorityTaskQueue {
253 const EMPTY_LIST: LinkedList<Rc<RefCell<Task>>> = LinkedList::new();
254 PriorityTaskQueue {
255 queues: [EMPTY_LIST; NO_PRIORITIES],
256 prio_bitmap: 0,
257 }
258 }
259
260 pub fn push(&mut self, task: Rc<RefCell<Task>>) {
262 let i = task.borrow().prio.into() as usize;
263 self.prio_bitmap |= (1 << i) as u64;
266 let queue = &mut self.queues[i];
267 queue.push_back(task);
268 }
269
270 fn pop_from_queue(&mut self, queue_index: usize) -> Option<Rc<RefCell<Task>>> {
271 let task = self.queues[queue_index].pop_front();
272 if self.queues[queue_index].is_empty() {
273 self.prio_bitmap &= !(1 << queue_index as u64);
274 }
275
276 task
277 }
278
279 fn remove_from_queue(
282 &mut self,
283 task_index: usize,
284 queue_index: usize,
285 ) -> Option<Rc<RefCell<Task>>> {
286 let queue = &mut self.queues[queue_index];
289 if queue.len() < task_index {
290 return None;
291 }
292
293 let mut split_list = queue.split_off(task_index);
295 let element = split_list.pop_front();
296 queue.append(&mut split_list);
297 if queue.is_empty() {
298 self.prio_bitmap &= !(1 << queue_index as u64);
299 }
300 element
301 }
302
303 pub fn is_empty(&self) -> bool {
305 self.prio_bitmap == 0
306 }
307
308 #[allow(dead_code)]
310 #[inline]
311 pub fn get_priority_bitmap(&self) -> &u64 {
312 &self.prio_bitmap
313 }
314
315 pub fn pop(&mut self) -> Option<Rc<RefCell<Task>>> {
317 let i = msb(self.prio_bitmap)?;
318
319 self.pop_from_queue(i as usize)
320 }
321
322 pub fn pop_with_prio(&mut self, prio: Priority) -> Option<Rc<RefCell<Task>>> {
324 let i = msb(self.prio_bitmap)?;
325
326 if i < u32::from(prio.into()) {
327 return None;
328 }
329
330 self.pop_from_queue(i as usize)
331 }
332
333 #[cfg(all(any(target_arch = "x86_64", target_arch = "riscv64"), feature = "smp"))]
335 pub fn get_highest_priority(&self) -> Priority {
336 let Some(i) = msb(self.prio_bitmap) else {
337 return IDLE_PRIO;
338 };
339
340 Priority::from(i.try_into().unwrap())
341 }
342
343 pub fn set_priority(&mut self, handle: TaskHandle, prio: Priority) -> Result<(), ()> {
345 let old_priority = handle.get_priority().into() as usize;
346 let index = self.queues[old_priority]
347 .iter()
348 .position(|current_task| current_task.borrow().id == handle.id)
349 .ok_or(())?;
350
351 let task = self.remove_from_queue(index, old_priority).ok_or(())?;
352 task.borrow_mut().prio = prio;
353 self.push(task);
354 Ok(())
355 }
356}
357
358#[cfg_attr(any(target_arch = "x86_64", target_arch = "aarch64"), repr(align(128)))]
360#[cfg_attr(
361 not(any(target_arch = "x86_64", target_arch = "aarch64")),
362 repr(align(64))
363)]
364pub(crate) struct Task {
365 pub id: TaskId,
367 pub status: TaskStatus,
369 pub prio: Priority,
371 pub last_stack_pointer: VirtAddr,
373 pub user_stack_pointer: VirtAddr,
375 pub last_fpu_state: arch::processor::FPUState,
377 pub core_id: CoreId,
379 pub stacks: TaskStacks,
381 pub object_map: Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
383 #[cfg(not(feature = "common-os"))]
385 pub tls: Option<Tls>,
386 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
388 pub root_page_table: usize,
389}
390
391pub(crate) trait TaskFrame {
392 fn create_stack_frame(&mut self, func: unsafe extern "C" fn(usize), arg: usize);
394}
395
396impl Task {
397 pub fn new(
398 tid: TaskId,
399 core_id: CoreId,
400 task_status: TaskStatus,
401 task_prio: Priority,
402 stacks: TaskStacks,
403 object_map: Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
404 ) -> Task {
405 debug!("Creating new task {tid} on core {core_id}");
406
407 Task {
408 id: tid,
409 status: task_status,
410 prio: task_prio,
411 last_stack_pointer: VirtAddr::zero(),
412 user_stack_pointer: VirtAddr::zero(),
413 last_fpu_state: arch::processor::FPUState::new(),
414 core_id,
415 stacks,
416 object_map,
417 #[cfg(not(feature = "common-os"))]
418 tls: None,
419 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
420 root_page_table: arch::create_new_root_page_table(),
421 }
422 }
423
424 pub fn new_idle(tid: TaskId, core_id: CoreId) -> Task {
425 debug!("Creating idle task {tid}");
426
427 static OBJECT_MAP: OnceCell<
429 Arc<RwSpinLock<HashMap<RawFd, Arc<async_lock::RwLock<Fd>>, RandomState>>>,
430 > = OnceCell::new();
431
432 if core_id == 0 {
433 OBJECT_MAP
434 .set(Arc::new(RwSpinLock::new(HashMap::<
435 RawFd,
436 Arc<async_lock::RwLock<Fd>>,
437 RandomState,
438 >::with_hasher(
439 RandomState::with_seeds(0, 0, 0, 0),
440 ))))
441 .unwrap_or_else(|_| unreachable!());
444 let objmap = OBJECT_MAP.get().unwrap().clone();
445 stdio::setup(&mut objmap.write());
446 }
447
448 #[cfg(not(feature = "common-os"))]
449 let tls = if cfg!(feature = "instrument-mcount") {
450 Tls::from_env().inspect(Tls::set_thread_ptr)
451 } else {
452 None
453 };
454
455 Task {
456 id: tid,
457 status: TaskStatus::Idle,
458 prio: IDLE_PRIO,
459 last_stack_pointer: VirtAddr::zero(),
460 user_stack_pointer: VirtAddr::zero(),
461 last_fpu_state: arch::processor::FPUState::new(),
462 core_id,
463 stacks: TaskStacks::from_boot_stacks(),
464 object_map: OBJECT_MAP.get().unwrap().clone(),
465 #[cfg(not(feature = "common-os"))]
466 tls,
467 #[cfg(all(target_arch = "x86_64", feature = "common-os"))]
468 root_page_table: *crate::scheduler::BOOT_ROOT_PAGE_TABLE.get().unwrap(),
469 }
470 }
471}
472
473struct BlockedTask {
480 task: Rc<RefCell<Task>>,
481 wakeup_time: Option<u64>,
482}
483
484impl BlockedTask {
485 pub fn new(task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) -> Self {
486 Self { task, wakeup_time }
487 }
488}
489
490pub(crate) struct BlockedTaskQueue {
491 list: LinkedList<BlockedTask>,
492}
493
494impl BlockedTaskQueue {
495 pub const fn new() -> Self {
496 Self {
497 list: LinkedList::new(),
498 }
499 }
500
501 fn mark_ready(task: &RefCell<Task>) {
502 let mut borrowed = task.borrow_mut();
503 debug!(
504 "Waking up task {} on core {}",
505 borrowed.id, borrowed.core_id
506 );
507
508 assert!(
509 borrowed.core_id == core_id(),
510 "Try to wake up task {} on the wrong core {} != {}",
511 borrowed.id,
512 borrowed.core_id,
513 core_id()
514 );
515
516 assert!(
517 borrowed.status == TaskStatus::Blocked,
518 "Trying to wake up task {} which is not blocked",
519 borrowed.id
520 );
521 borrowed.status = TaskStatus::Ready;
522 }
523
524 pub fn add(&mut self, task: Rc<RefCell<Task>>, wakeup_time: Option<u64>) {
526 {
527 let mut borrowed = task.borrow_mut();
529 debug!("Blocking task {}", borrowed.id);
530
531 assert_eq!(
532 borrowed.status,
533 TaskStatus::Running,
534 "Trying to block task {} which is not running",
535 borrowed.id
536 );
537 borrowed.status = TaskStatus::Blocked;
538 }
539
540 let new_node = BlockedTask::new(task, wakeup_time);
541
542 if let Some(wt) = wakeup_time {
544 let mut cursor = self.list.cursor_front_mut();
545
546 while let Some(node) = cursor.current() {
547 let node_wakeup_time = node.wakeup_time;
548 if node_wakeup_time.is_none() || wt < node_wakeup_time.unwrap() {
549 cursor.insert_before(new_node);
550
551 create_timer_abs(Source::Scheduler, wt);
552 return;
553 }
554
555 cursor.move_next();
556 }
557
558 create_timer_abs(Source::Scheduler, wt);
559 }
560
561 self.list.push_back(new_node);
562 }
563
564 pub fn custom_wakeup(&mut self, task: TaskHandle) -> Rc<RefCell<Task>> {
566 let mut first_task = true;
567 let mut cursor = self.list.cursor_front_mut();
568
569 while let Some(node) = cursor.current() {
571 if node.task.borrow().id == task.get_id() {
572 let task_ref = node.task.clone();
574 cursor.remove_current();
575
576 if first_task
579 && let Some(wakeup) = cursor
580 .current()
581 .map_or_else(|| None, |node| node.wakeup_time)
582 {
583 create_timer_abs(Source::Scheduler, wakeup);
584 }
585
586 Self::mark_ready(&task_ref);
588
589 return task_ref;
590 }
591
592 first_task = false;
593 cursor.move_next();
594 }
595
596 unreachable!();
597 }
598
599 pub fn handle_waiting_tasks(&mut self, ready_queue: &mut PriorityTaskQueue) {
604 let time = arch::processor::get_timer_ticks();
606
607 let newly_ready_tasks = self.list.extract_if(|blocked_task| {
611 blocked_task
612 .wakeup_time
613 .is_some_and(|wakeup_time| wakeup_time < time)
614 });
615
616 for task in newly_ready_tasks {
617 Self::mark_ready(&task.task);
618 ready_queue.push(task.task);
619 }
620
621 let new_task_wakeup_time = self.list.front().and_then(|task| task.wakeup_time);
622
623 if let Some(wakeup) = new_task_wakeup_time {
624 create_timer_abs(Source::Scheduler, wakeup);
625 }
626 }
627}