use crate::arch::x86_64::is_int_enabled;
use crate::machine::interrupt::{irq_restore, irq_save};
use crate::proc::sync::*;
use crate::proc::task::*;
use alloc::collections::VecDeque;
use core::sync::atomic::AtomicBool;
use core::sync::atomic::Ordering;
pub static GLOBAL_SCHEDULER: L2Sync<Scheduler> = L2Sync::new(Scheduler::new());
pub static NEED_RESCHEDULE: AtomicBool = AtomicBool::new(false);
#[inline(always)]
#[allow(non_snake_case)]
pub fn SET_NEED_RESCHEDULE() -> bool {
NEED_RESCHEDULE.swap(true, Ordering::Relaxed)
}
#[inline(always)]
#[allow(non_snake_case)]
pub fn CLEAR_NEED_RESCHEDULE() -> bool {
NEED_RESCHEDULE.swap(false, Ordering::Relaxed)
}
pub struct Scheduler {
pub run_queue: VecDeque<TaskId>,
pub need_schedule: bool,
}
impl Scheduler {
pub const MIN_TASK_CAP: usize = 16;
pub const fn new() -> Self {
return Self {
run_queue: VecDeque::new(),
need_schedule: false,
};
}
pub fn insert_task(&mut self, tid: TaskId) {
self.run_queue.push_back(tid);
}
pub fn try_remove(&mut self, _tid: TaskId) {
todo!("not implemented");
}
pub unsafe fn try_reschedule() {
debug_assert!(is_int_enabled());
let r = NEED_RESCHEDULE.compare_exchange(
true,
false,
Ordering::Relaxed,
Ordering::Relaxed,
);
if r != Ok(true) {
return;
}
Self::do_schedule();
}
pub unsafe fn do_schedule() {
let me = Task::current().unwrap();
let next_task;
let next_tid;
{
let r = irq_save();
let sched = GLOBAL_SCHEDULER.get_ref_mut_unguarded();
if sched.run_queue.is_empty() && me.state == TaskState::Run {
irq_restore(r);
return;
}
next_tid = sched.run_queue.pop_front().expect("no runnable task");
next_task = next_tid.get_task_ref_mut();
debug_assert_eq!(next_task.state, TaskState::Run);
if me.state == TaskState::Run {
sched.run_queue.push_back(me.taskid());
}
irq_restore(r);
}
if me.taskid() == next_task.taskid() {
return;
}
unsafe {
context_swap(
&(me.context) as *const _ as u64,
&(next_task.context) as *const _ as u64,
);
}
}
pub fn yield_cpu() {
debug_assert!(is_int_enabled());
ENTER_L2();
unsafe {
Self::do_schedule();
}
LEAVE_L2();
}
pub unsafe fn kickoff() {
let irq = irq_save();
let sched = GLOBAL_SCHEDULER.get_ref_mut_unguarded();
let tid = sched
.run_queue
.pop_front()
.expect("run queue empty, can't start");
let first_task = tid.get_task_ref_mut();
irq_restore(irq);
ENTER_L2();
unsafe {
context_swap_to(&(first_task.context) as *const _ as u64);
}
}
}