Add comments

This commit is contained in:
Quentin Legot 2023-05-24 22:00:16 +02:00
parent c2d51d72f1
commit 8a389ea9d3

View File

@ -119,6 +119,9 @@ pub struct ThreadManager {
obj_addrs: ObjAddr, obj_addrs: ObjAddr,
/// If true, enables debug mode /// If true, enables debug mode
debug: bool, debug: bool,
/// Temporary field, to be removed when virtual memory will be available to use.
///
/// A value to know where the next starting thread should have its stack pointer
sp_max: u64, sp_max: u64,
} }
@ -136,19 +139,19 @@ impl ThreadManager {
} }
} }
/// Mark a thread as aready, but not necessarily running yet. /// Mark `thread` as ready, but not necessarily running yet.
/// ///
/// Put it in the ready list, for later scheduling onto the CPU. /// Put it in the ready list, for later scheduling onto the CPU.
/// ///
/// ## Pamameter /// ## Pamameter
/// ///
/// **thread** is the thread to be put on the read list /// **thread** is the thread to be put on the ready list
pub fn ready_to_run(&mut self, thread: ThreadRef) { pub fn ready_to_run(&mut self, thread: ThreadRef) {
self.ready_list.push(thread); self.ready_list.push(thread);
} }
/// Return the next thread to be scheduled onto the CPU. /// Return the next thread to be scheduled onto the CPU.
/// If there are no ready threads, return Option::None /// If there are no ready threads, return `Option::None`
/// ///
/// Thread is removed from the ready list. /// Thread is removed from the ready list.
/// ///
@ -157,12 +160,12 @@ impl ThreadManager {
self.ready_list.pop() self.ready_list.pop()
} }
/// Dispatch the CPU to next_thread. Save the state of the old thread /// Dispatch the CPU to `next_thread`. Save the state of the old thread
/// and load the state of the new thread. /// and load the state of the new thread.
/// ///
/// We assume the state of the previously running thread has already been changed from running to blocked or ready. /// We assume the state of the previously running thread has already been changed from running to blocked or ready.
/// ///
/// Global variable g_current_thread become next_thread /// Variable `g_current_thread` become next_thread
/// ///
/// ## Parameter /// ## Parameter
/// ///
@ -187,6 +190,14 @@ impl ThreadManager {
} }
/// Start a thread, attaching it to a process /// Start a thread, attaching it to a process
///
/// ## Parameter
///
/// **thread** thread to start
/// **owner** process owner of thread (after the execution of this method)
/// **func_pc** pc the thread
/// **sp_loc** stack pointer of the thread, to remove (or move) when mmu will be completed
/// **argument** value to be place on register[10]
pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) { pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) {
self.debug(format!("starting thread \"{}\"", thread.borrow().get_name())); self.debug(format!("starting thread \"{}\"", thread.borrow().get_name()));
@ -201,6 +212,11 @@ impl ThreadManager {
} }
/// Wait for another thread to finish its execution /// Wait for another thread to finish its execution
///
/// If the thread you want to wait doesn't exist (isn't alive), execution will resume.
/// Otherwise, CPU is dispatch to next alive thread if any.
///
/// When the thread you want to join finish, it place the waiting thread (self) in ready list
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) { pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
let waiting_for = Rc::clone(&waiting_for); let waiting_for = Rc::clone(&waiting_for);
if self.get_g_alive().contains(&waiting_for) { if self.get_g_alive().contains(&waiting_for) {
@ -211,10 +227,19 @@ impl ThreadManager {
/// Relinquish the CPU if any other thread is runnable. /// Relinquish the CPU if any other thread is runnable.
/// ///
/// Cannot use yield as a function name -> reserved name in rust /// If so, put the current thread at the end of the ready list, so it'll be re-scheduled in the future.
///
/// **Returns** immediately if there's no other thread ready or return when the current thread has been switched.
///
/// Interruptions are disabled during the process, so all the process of looking for a next thread and switching to it is atomic,
/// and is place at its old status at the end of the method.
///
/// Cannot use `yield` as a function name -> reserved name in rust
/// ///
/// ## Parameters /// ## Parameters
/// ///
/// **machine** RISC-V simulator
/// **thread** current thread to be relinquish
/// **is_ready** true if **thread** should be readded to ready_to_run list, false otherwise. Typically false when joining per example /// **is_ready** true if **thread** should be readded to ready_to_run list, false otherwise. Typically false when joining per example
pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef, is_ready: bool) { pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef, is_ready: bool) {
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff); let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
@ -231,7 +256,16 @@ impl ThreadManager {
machine.interrupt.set_status(old_status); machine.interrupt.set_status(old_status);
} }
/// Put the thread to sleep and relinquish the processor /// Put the thread to sleep and relinquish the processor because the current thread is blocked (Semaphore, Lock, Condition) or because it finished its execution
///
/// Another thread will eventually wake it up and put it back to ready list after it has been unblocked.
///
/// Behavior now: At the moment, disk isn't fully develop and not integrated to burritos, so if there's no ready thread, then we stop the OS.
///
/// Behaviour in the future: If there are no threads on the ready list, that means there is no thread to run,
/// we assume this is because at least one thread is waiting for I/O [`interrupt`](crate::simulator::interrupt::Interrupt) (the only reason a new thread can become ready at this point).
///
/// We also assume interruption are already disabled, becuase it's called from a synchronization routine for interrupt should be disabled.
pub fn thread_sleep(&mut self, machine: &mut Machine, thread: ThreadRef) { pub fn thread_sleep(&mut self, machine: &mut Machine, thread: ThreadRef) {
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread); debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff); debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
@ -252,6 +286,12 @@ impl ThreadManager {
} }
/// Finish the execution of the thread and prepare its deallocation /// Finish the execution of the thread and prepare its deallocation
///
/// Called by the thread itself when it finish its execution ([`Exit`](super::exception::SC_EXIT) exception).
///
/// We remove the thread from the alive list, and rustc deallocate the thread itself(behaviour different than Nachos)
///
/// Interruption are disabled to assume atomicity.
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef, exit_code: i64) { pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef, exit_code: i64) {
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
assert!(self.g_alive.remove(Rc::clone(&thread))); assert!(self.g_alive.remove(Rc::clone(&thread)));
@ -265,6 +305,8 @@ impl ThreadManager {
} }
/// Save the CPU state of a user program on a context switch. /// Save the CPU state of a user program on a context switch.
///
/// Save PC and registers
pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) { pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) {
let mut t = thread.borrow_mut(); let mut t = thread.borrow_mut();
for i in 0..NUM_INT_REGS { for i in 0..NUM_INT_REGS {
@ -277,6 +319,8 @@ impl ThreadManager {
} }
/// Restore the CPU state of a user program on a context switch. /// Restore the CPU state of a user program on a context switch.
///
/// Restore PC and registers
pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) { pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) {
let t: Ref<_> = thread.borrow(); let t: Ref<_> = thread.borrow();
for i in 0..NUM_INT_REGS { for i in 0..NUM_INT_REGS {
@ -371,7 +415,9 @@ impl ThreadManager {
Ok(MachineOk::Ok) Ok(MachineOk::Ok)
} }
/// Wake up a waiter if necessary, or release it if no thread is waiting. /// Release lock hold by current thread and wake up a waiter if necessary, placing it on ready list, this thread now hold the lock.
///
/// If no thread is waiting for the lock, the lock is released
pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> { pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
let current_thread = match self.get_g_current_thread() { let current_thread = match self.get_g_current_thread() {
@ -402,12 +448,12 @@ impl ThreadManager {
Ok(MachineOk::Ok) Ok(MachineOk::Ok)
} }
/// Currently running thread /// Return currently running thread
pub fn get_g_current_thread(&mut self) -> &Option<ThreadRef> { pub fn get_g_current_thread(&mut self) -> &Option<ThreadRef> {
&self.g_current_thread &self.g_current_thread
} }
/// List of alive threads /// Return list of alive threads
pub fn get_g_alive(&mut self) -> &mut List<ThreadRef> { pub fn get_g_alive(&mut self) -> &mut List<ThreadRef> {
&mut self.g_alive &mut self.g_alive
} }