diff --git a/src/kernel/thread_manager.rs b/src/kernel/thread_manager.rs index 65da837..452d51b 100644 --- a/src/kernel/thread_manager.rs +++ b/src/kernel/thread_manager.rs @@ -119,6 +119,9 @@ pub struct ThreadManager { obj_addrs: ObjAddr, /// If true, enables debug mode 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, } @@ -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. /// /// ## 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) { self.ready_list.push(thread); } /// 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. /// @@ -157,12 +160,12 @@ impl ThreadManager { 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. /// /// 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 /// @@ -187,6 +190,14 @@ impl ThreadManager { } /// 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>, func_pc: u64, sp_loc: u64, argument: i64) { self.debug(format!("starting thread \"{}\"", thread.borrow().get_name())); @@ -201,6 +212,11 @@ impl ThreadManager { } /// 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) { let waiting_for = Rc::clone(&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. /// - /// 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 /// + /// **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 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); @@ -231,7 +256,16 @@ impl ThreadManager { 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) { debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread); 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 + /// + /// 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) { let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); 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 PC and registers pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) { let mut t = thread.borrow_mut(); 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 PC and registers pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) { let t: Ref<_> = thread.borrow(); for i in 0..NUM_INT_REGS { @@ -371,7 +415,9 @@ impl ThreadManager { 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 { let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); let current_thread = match self.get_g_current_thread() { @@ -402,12 +448,12 @@ impl ThreadManager { Ok(MachineOk::Ok) } - /// Currently running thread + /// Return currently running thread pub fn get_g_current_thread(&mut self) -> &Option { &self.g_current_thread } - /// List of alive threads + /// Return list of alive threads pub fn get_g_alive(&mut self) -> &mut List { &mut self.g_alive }