Sync computers
This commit is contained in:
148
src/scheduler.rs
148
src/scheduler.rs
@@ -3,50 +3,29 @@
|
||||
//!
|
||||
//! This module exposes the global process table, the scheduler initialization
|
||||
//! and a simple round-robin scheduler used by the kernel.
|
||||
use core::{arch::riscv64::wfi, array, cell::LazyCell, time::Duration};
|
||||
use core::{arch::riscv64::wfi, cell::LazyCell, ops::Bound};
|
||||
|
||||
use alloc::{boxed::Box, string::String};
|
||||
use hashbrown::HashMap;
|
||||
use alloc::{boxed::Box, collections::BTreeMap};
|
||||
use log::info;
|
||||
|
||||
use crate::{
|
||||
process::{create_process, ExecutionContext, Process, ProcessState},
|
||||
process::{ExecutionContext, Process, ProcessState},
|
||||
sync::Mutex,
|
||||
time,
|
||||
};
|
||||
|
||||
/// Maximum number of simultaneous processes supported by the kernel.
|
||||
pub const PROCESS_COUNT: usize = 16;
|
||||
#[derive(Debug)]
|
||||
pub struct Scheduler {
|
||||
pub next_pid: u64,
|
||||
pub active_pid: u64,
|
||||
pub process_table: BTreeMap<u64, Box<Process>>,
|
||||
}
|
||||
|
||||
/// Currently active PID.
|
||||
///
|
||||
/// Updated by the scheduler when switching contexts.
|
||||
pub static mut ACTIVE_PID: usize = 0;
|
||||
/// Global process table stored in a lazily-initialized container.
|
||||
///
|
||||
/// Each entry represents a process slot which may be `Dead`, `Activable`,
|
||||
/// `Active` or `Asleep`.
|
||||
pub static mut PROCESS_TABLE: LazyCell<[Process; PROCESS_COUNT]> = LazyCell::new(|| {
|
||||
array::from_fn(|_| Process {
|
||||
pid: -1,
|
||||
name: String::new(),
|
||||
state: ProcessState::Dead,
|
||||
wake_time: Duration::new(0, 0),
|
||||
ctx: ExecutionContext {
|
||||
ra: core::ptr::null(),
|
||||
sp: core::ptr::null(),
|
||||
gp: 0,
|
||||
tp: 0,
|
||||
a: [0; _],
|
||||
t: [0; _],
|
||||
s: [0; _],
|
||||
mepc: core::ptr::null(),
|
||||
mstatus: 0,
|
||||
},
|
||||
stack: [0; _],
|
||||
entry: None,
|
||||
fd_table: HashMap::new()
|
||||
})
|
||||
});
|
||||
pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| Scheduler {
|
||||
next_pid: 0,
|
||||
active_pid: 0,
|
||||
process_table: BTreeMap::new(),
|
||||
}));
|
||||
|
||||
/// Idle loop executed when there is no runnable process.
|
||||
///
|
||||
@@ -61,53 +40,60 @@ pub fn idle() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the scheduler and create the idle process.
|
||||
///
|
||||
/// Marks all process slots as `Dead` then creates the idle process and sets
|
||||
/// it as the active process.
|
||||
pub fn scheduler_init() {
|
||||
info!("scheduler init");
|
||||
for pid in 0..PROCESS_COUNT {
|
||||
impl Scheduler {
|
||||
/// Initialize the scheduler and create the idle process.
|
||||
///
|
||||
/// Marks all process slots as `Dead` then creates the idle process and sets
|
||||
/// it as the active process.
|
||||
pub fn init(&mut self) {
|
||||
info!("scheduler init");
|
||||
self.create_process(Box::new(idle), "idle");
|
||||
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
|
||||
}
|
||||
|
||||
/// Round-robin scheduler used to select the next runnable process.
|
||||
///
|
||||
/// Saves the provided interrupt context into the previous process slot and
|
||||
/// updates `ACTIVE_PID` to point to the chosen process. This function does
|
||||
/// not return but instead updates `interrupt_state` to the context of the
|
||||
/// next process to run.
|
||||
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
|
||||
// info!("scheduler");
|
||||
unsafe {
|
||||
PROCESS_TABLE[pid].state = ProcessState::Dead;
|
||||
let prev_pid = self.active_pid;
|
||||
if let Some(previous_process) = self.process_table.get_mut(&prev_pid) {
|
||||
previous_process.ctx = **interrupt_state;
|
||||
|
||||
if previous_process.state == ProcessState::Active {
|
||||
previous_process.state = ProcessState::Activable;
|
||||
}
|
||||
}
|
||||
|
||||
let mut current_process_iter = self
|
||||
.process_table
|
||||
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
|
||||
|
||||
self.active_pid = loop {
|
||||
if let Some((pid, current_process)) = current_process_iter.next() {
|
||||
if current_process.state == ProcessState::Asleep
|
||||
&& time::elapsed_time_since_startup() > current_process.wake_time
|
||||
{
|
||||
current_process.state = ProcessState::Activable;
|
||||
}
|
||||
if current_process.state == ProcessState::Activable {
|
||||
current_process.state = ProcessState::Active;
|
||||
*interrupt_state = &raw mut current_process.ctx;
|
||||
break *pid;
|
||||
};
|
||||
} else {
|
||||
current_process_iter = self
|
||||
.process_table
|
||||
.range_mut((Bound::Unbounded, Bound::Included(prev_pid)))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
create_process(Box::new(idle), "idle");
|
||||
unsafe {
|
||||
PROCESS_TABLE[0].state = ProcessState::Active;
|
||||
}
|
||||
}
|
||||
|
||||
/// Round-robin scheduler used to select the next runnable process.
|
||||
///
|
||||
/// Saves the provided interrupt context into the previous process slot and
|
||||
/// updates `ACTIVE_PID` to point to the chosen process. This function does
|
||||
/// not return but instead updates `interrupt_state` to the context of the
|
||||
/// next process to run.
|
||||
pub fn scheduler_without_ret(interrupt_state: &mut *mut ExecutionContext) {
|
||||
// info!("scheduler");
|
||||
unsafe {
|
||||
let prev_pid = ACTIVE_PID;
|
||||
PROCESS_TABLE[prev_pid].ctx = **interrupt_state;
|
||||
|
||||
if PROCESS_TABLE[prev_pid].state == ProcessState::Active {
|
||||
PROCESS_TABLE[prev_pid].state = ProcessState::Activable;
|
||||
}
|
||||
|
||||
loop {
|
||||
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Asleep
|
||||
&& time::elapsed_time_since_startup() > PROCESS_TABLE[ACTIVE_PID].wake_time
|
||||
{
|
||||
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
|
||||
}
|
||||
ACTIVE_PID = (ACTIVE_PID + 1) % PROCESS_COUNT;
|
||||
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Activable {
|
||||
break;
|
||||
}
|
||||
}
|
||||
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
|
||||
|
||||
*interrupt_state = &raw mut PROCESS_TABLE[ACTIVE_PID].ctx
|
||||
pub fn get_current_process(&mut self) -> &mut Process {
|
||||
self.process_table.get_mut(&self.active_pid).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user