2023-04-20 00:05:37 +02:00
//! # Thread manager
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! This module describes the data structure and the methods used for thread scheduling
2023-04-20 15:15:11 +02:00
//! in the BurritOS operating system. A struct named [`ThreadManager`] holds the list of
//! all existing [`Thread`] instances and synchronization objects, such as
//! [`Lock`](crate::kernel::synch::Lock),
//! [`Semaphore`](crate::kernel::synch::Semaphore) and
//! [`Condition`](crate::kernel::synch::Condition).
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ## Purpose
2023-04-20 14:43:15 +02:00
//!
2023-04-20 15:15:11 +02:00
//! [`ThreadManager`] holds the state of the system processes using the following subcomponents:
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ### Two lists of threads
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! - **ready_list**: The list of threads ready to be executed
//! - **g_alive**: The list of currently executing threads
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! The difference between the two above lists lies in the state of the threads in question.
//! Ready threads have just been enqueued. They are not being executed yet. The second list is
//! needed because many threads may be executing at a given time. However, only a single thread
//! can be handled by the machine at a time. The system thus needs to keep in memory the alive
//! threads in case the currently running thread finishes or gets rescheduled.
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ### A list of synchronization objects
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! Locks, Semaphores and Conditions allow resource sharing among running threads. Since resources
//! can only be accessed by a single thread at a time, we need data structures to signal other
2023-04-20 15:15:11 +02:00
//! threads that a resource may be busy or unavailable; say for example that:
//!
//! - Thread **A** wants to write to a file while **B** is currently reading said file.
//! - Thread **A** mutating the state of the file could cause issues for **B**.
//! - Therefore **B** needs to lock the file in question to avoid such issues.
//! - Thread **A** will have to wait for **B** to finish reading the file.
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! These synchronization objects are held in an instance of the ObjAddr structure held by
//! ThreadManager. Their state is mutated depending on the actions of the currently running thread
2023-04-20 15:15:11 +02:00
//! through methods such as [`ThreadManager::sem_p`].
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ## Usage
2023-04-20 14:43:15 +02:00
//!
2023-04-20 15:15:11 +02:00
//! [`ThreadManager`] is thought as a subcomponent of the [`System`](crate::kernel::system::System) struct.
//! Instanciating [`System`](crate::kernel::system::System) will automatically instanciate a [`ThreadManager`]
2023-04-20 14:43:15 +02:00
//!
2023-04-20 15:15:11 +02:00
//! Manually loading a [`Thread`] into [`ThreadManager`] to execute a program with BurritOS could look like
2023-04-20 00:05:37 +02:00
//! this:
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ```
//! fn load_thread_manually(args: ...) {
//! let mut system = System::new(args.debug);
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! let thread_exec = Thread::new(args.executable.as_str());
//! let thread_exec = Rc::new(RefCell::new(thread_exec));
//! system.get_thread_manager().get_g_alive().push(Rc::clone(&thread_exec));
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! let owner1 = Process { num_thread: 0 };
//! let owner1 = Rc::new(RefCell::new(owner1));
//! system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr, -1);
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! let to_run = system.get_thread_manager().find_next_to_run().unwrap();
//! system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! machine.run(&mut system);
//! }
//! ```
2023-04-20 14:43:15 +02:00
//!
2023-04-20 00:05:37 +02:00
//! ## Imports
2023-04-20 14:43:15 +02:00
//!
2023-04-20 15:15:11 +02:00
//! The [`List`] and [`ObjAddr`] submodules used in this module are defined in the utility
//! module. The source code of [`ObjAddr`] has been decoupled from thread_manager in an effort
2023-04-20 00:05:37 +02:00
//! to keep this module concise.
2023-04-19 23:33:37 +02:00
use std ::{
rc ::Rc ,
cell ::{
RefCell ,
Ref
}
} ;
use crate ::{
utility ::{
list ::List ,
objaddr ::ObjAddr
} ,
simulator ::{
machine ::{
NUM_INT_REGS ,
NUM_FP_REGS ,
Machine
} ,
interrupt ::InterruptStatus ,
error ::{
MachineOk ,
MachineError
}
2023-04-20 11:31:25 +02:00
} ,
kernel ::{
thread ::Thread ,
process ::Process
2023-04-19 23:33:37 +02:00
}
} ;
2023-03-08 21:10:51 +01:00
2023-04-20 11:31:25 +02:00
/// Using this type alias to simplify struct and method definitions
2023-04-23 15:11:06 +02:00
pub type ThreadRef = Rc < RefCell < Thread > > ;
2023-04-20 11:31:25 +02:00
2023-03-14 15:16:40 +01:00
/// # Thread manager
///
/// An instance of this struct is responsible for managing threads on behalf of the system
2023-03-08 21:10:51 +01:00
#[ derive(PartialEq) ]
2023-03-13 21:47:06 +01:00
pub struct ThreadManager {
2023-03-14 15:16:40 +01:00
/// Current running thread
2023-04-20 11:31:25 +02:00
pub g_current_thread : Option < ThreadRef > ,
2023-03-14 15:16:40 +01:00
/// The list of alive threads
2023-04-20 11:31:25 +02:00
pub g_alive : List < ThreadRef > ,
2023-03-16 14:52:53 +01:00
/// Thread in ready state waiting to become active
2023-04-20 11:31:25 +02:00
ready_list : List < ThreadRef > ,
2023-04-19 23:38:58 +02:00
/// List of objects created by the thread manager (such as Locks and Semaphores)
2023-04-13 02:05:21 +02:00
obj_addrs : ObjAddr ,
2023-04-19 23:38:58 +02:00
/// If true, enables debug mode
2023-05-09 17:01:52 +02:00
debug : bool ,
2023-05-24 22:00:16 +02:00
/// 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
2023-05-09 17:01:52 +02:00
sp_max : u64 ,
2023-03-08 21:10:51 +01:00
}
2023-03-13 21:47:06 +01:00
impl ThreadManager {
2023-03-08 21:10:51 +01:00
2023-03-14 15:16:40 +01:00
/// Thread manager constructor
2023-04-13 02:05:21 +02:00
pub fn new ( debug : bool ) -> Self {
2023-03-08 21:10:51 +01:00
Self {
2023-04-19 23:33:37 +02:00
g_current_thread : Option ::None ,
2023-03-21 22:40:49 +01:00
g_alive : List ::default ( ) ,
ready_list : List ::default ( ) ,
2023-04-12 14:49:08 +02:00
obj_addrs : ObjAddr ::init ( ) ,
2023-05-09 17:01:52 +02:00
debug ,
sp_max : 0
2023-03-16 14:52:53 +01:00
}
}
2023-05-24 22:00:16 +02:00
/// Mark `thread` as ready, but not necessarily running yet.
2023-03-16 14:52:53 +01:00
///
/// Put it in the ready list, for later scheduling onto the CPU.
///
/// ## Pamameter
///
2023-05-24 22:00:16 +02:00
/// **thread** is the thread to be put on the ready list
2023-04-20 11:31:25 +02:00
pub fn ready_to_run ( & mut self , thread : ThreadRef ) {
2023-03-16 14:52:53 +01:00
self . ready_list . push ( thread ) ;
}
/// Return the next thread to be scheduled onto the CPU.
2023-05-24 22:00:16 +02:00
/// If there are no ready threads, return `Option::None`
2023-03-16 14:52:53 +01:00
///
/// Thread is removed from the ready list.
///
/// **return** Thread thread to be scheduled
2023-04-20 11:31:25 +02:00
pub fn find_next_to_run ( & mut self ) -> Option < ThreadRef > {
2023-03-16 14:52:53 +01:00
self . ready_list . pop ( )
}
2023-05-24 22:00:16 +02:00
/// Dispatch the CPU to `next_thread`. Save the state of the old thread
2023-03-16 14:52:53 +01:00
/// 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.
///
2023-05-24 22:00:16 +02:00
/// Variable `g_current_thread` become next_thread
2023-03-16 14:52:53 +01:00
///
/// ## Parameter
///
/// **next_thread** thread to dispatch to the CPU
2023-04-20 11:31:25 +02:00
pub fn switch_to ( & mut self , machine : & mut Machine , next_thread : ThreadRef ) {
2023-04-19 23:24:51 +02:00
if let Some ( old_thread ) = self . get_g_current_thread ( ) {
let old_thread = old_thread . clone ( ) ;
self . thread_save_processor_state ( machine , old_thread . clone ( ) ) ;
// old_thread.save_simulator_state();
if old_thread ! = next_thread {
self . debug ( format! ( " switching from \" {} \" to \" {} \" " , old_thread . borrow ( ) . get_name ( ) , next_thread . borrow ( ) . get_name ( ) ) ) ;
2023-03-27 22:20:29 +02:00
self . thread_restore_processor_state ( machine , Rc ::clone ( & next_thread ) ) ;
// next_thread.restore_simulator_state();
2023-04-19 23:24:51 +02:00
debug_assert! ( ! self . ready_list . contains ( & next_thread ) ) ;
2023-03-27 22:20:29 +02:00
self . set_g_current_thread ( Some ( next_thread ) ) ;
2023-03-16 14:52:53 +01:00
}
2023-04-19 23:24:51 +02:00
} else {
self . thread_restore_processor_state ( machine , Rc ::clone ( & next_thread ) ) ;
// next_thread.restore_simulator_state();
self . set_g_current_thread ( Some ( next_thread ) ) ;
2023-03-08 21:10:51 +01:00
}
}
/// Start a thread, attaching it to a process
2023-05-24 22:00:16 +02:00
///
/// ## 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]
2023-04-20 11:31:25 +02:00
pub fn start_thread ( & mut self , thread : ThreadRef , owner : Rc < RefCell < Process > > , func_pc : u64 , sp_loc : u64 , argument : i64 ) {
2023-04-23 15:11:06 +02:00
self . debug ( format! ( " starting thread \" {} \" " , thread . borrow ( ) . get_name ( ) ) ) ;
2023-03-09 14:00:42 +01:00
let mut thread_m = thread . borrow_mut ( ) ;
2023-03-13 20:55:46 +01:00
assert_eq! ( thread_m . process , Option ::None ) ;
2023-04-13 02:05:21 +02:00
thread_m . process = Option ::Some ( Rc ::clone ( & owner ) ) ;
2023-04-02 19:55:06 +02:00
let ptr = sp_loc ; // todo addrspace
2023-03-09 14:00:42 +01:00
thread_m . init_thread_context ( func_pc , ptr , argument ) ;
2023-04-13 02:05:21 +02:00
owner . borrow_mut ( ) . num_thread + = 1 ;
2023-03-09 14:00:42 +01:00
self . get_g_alive ( ) . push ( Rc ::clone ( & thread ) ) ;
2023-03-16 14:52:53 +01:00
self . ready_to_run ( Rc ::clone ( & thread ) ) ;
2023-03-08 21:10:51 +01:00
}
/// Wait for another thread to finish its execution
2023-05-24 22:00:16 +02:00
///
/// 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
2023-04-20 11:31:25 +02:00
pub fn thread_join ( & mut self , machine : & mut Machine , waiter : ThreadRef , waiting_for : ThreadRef ) {
2023-04-13 02:05:21 +02:00
let waiting_for = Rc ::clone ( & waiting_for ) ;
2023-04-23 15:11:06 +02:00
if self . get_g_alive ( ) . contains ( & waiting_for ) {
waiting_for . borrow_mut ( ) . join_thread . push ( Rc ::clone ( & waiter ) ) ;
self . thread_yield ( machine , Rc ::clone ( & waiter ) , false ) ;
2023-03-08 21:10:51 +01:00
}
}
/// Relinquish the CPU if any other thread is runnable.
///
2023-05-24 22:00:16 +02:00
/// 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
2023-04-23 15:11:06 +02:00
///
/// ## Parameters
///
2023-05-24 22:00:16 +02:00
/// **machine** RISC-V simulator
/// **thread** current thread to be relinquish
2023-04-23 15:11:06 +02:00
/// **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 ) {
2023-03-16 14:52:53 +01:00
let old_status = machine . interrupt . set_status ( crate ::simulator ::interrupt ::InterruptStatus ::InterruptOff ) ;
2023-04-13 02:05:21 +02:00
2023-04-23 15:11:06 +02:00
self . debug ( format! ( " Yeilding thread: \" {} \" " , thread . borrow ( ) . get_name ( ) ) ) ;
2023-04-13 02:05:21 +02:00
debug_assert_eq! ( & Option ::Some ( Rc ::clone ( & thread ) ) , self . get_g_current_thread ( ) ) ;
2023-03-16 14:52:53 +01:00
let next_thread = self . find_next_to_run ( ) ;
2023-03-15 15:20:20 +01:00
if let Some ( next_thread ) = next_thread {
2023-04-23 15:11:06 +02:00
if is_ready {
self . ready_to_run ( thread ) ;
}
2023-03-16 14:52:53 +01:00
self . switch_to ( machine , next_thread ) ;
2023-03-11 14:48:56 +01:00
}
2023-03-16 14:52:53 +01:00
machine . interrupt . set_status ( old_status ) ;
2023-03-08 21:10:51 +01:00
}
2023-05-24 22:00:16 +02:00
/// 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.
2023-04-20 11:31:25 +02:00
pub fn thread_sleep ( & mut self , machine : & mut Machine , thread : ThreadRef ) {
2023-04-05 12:01:31 +02:00
debug_assert_eq! ( Option ::Some ( Rc ::clone ( & thread ) ) , self . g_current_thread ) ;
debug_assert_eq! ( machine . interrupt . get_status ( ) , InterruptStatus ::InterruptOff ) ;
2023-03-16 14:52:53 +01:00
2023-04-23 15:11:06 +02:00
self . debug ( format! ( " Sleeping thread {} " , thread . borrow ( ) . get_name ( ) ) ) ;
2023-03-16 14:52:53 +01:00
let mut next_thread = self . find_next_to_run ( ) ;
2023-03-15 15:20:20 +01:00
while next_thread . is_none ( ) {
eprintln! ( " Nobody to run => idle " ) ;
machine . interrupt . idle ( ) ;
2023-05-09 18:08:44 +02:00
if let Some ( t ) = self . find_next_to_run ( ) {
next_thread = Some ( t ) ;
} else {
panic! ( " Couldn't find next thread to run. \n Shutting down... " ) ;
}
2023-03-13 20:55:46 +01:00
}
2023-03-16 14:52:53 +01:00
self . switch_to ( machine , Rc ::clone ( & next_thread . unwrap ( ) ) ) ;
2023-03-09 12:08:33 +01:00
}
/// Finish the execution of the thread and prepare its deallocation
2023-05-24 22:00:16 +02:00
///
/// 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.
2023-05-09 23:16:16 +02:00
pub fn thread_finish ( & mut self , machine : & mut Machine , thread : ThreadRef , exit_code : i64 ) {
2023-03-15 15:20:20 +01:00
let old_status = machine . interrupt . set_status ( InterruptStatus ::InterruptOff ) ;
2023-04-19 23:38:58 +02:00
assert! ( self . g_alive . remove ( Rc ::clone ( & thread ) ) ) ;
2023-05-09 23:16:16 +02:00
self . debug ( format! ( " Finishing thread {} with code {} " , thread . borrow ( ) . get_name ( ) , exit_code ) ) ;
2023-03-15 15:20:20 +01:00
// g_objets_addrs->removeObject(self.thread) // a ajouté plus tard
2023-04-23 15:11:06 +02:00
for ( _ , el ) in thread . borrow ( ) . join_thread . iter ( ) . enumerate ( ) {
self . ready_to_run ( Rc ::clone ( & el ) ) ;
}
2023-03-15 17:57:53 +01:00
self . thread_sleep ( machine , Rc ::clone ( & thread ) ) ;
2023-03-15 15:20:20 +01:00
machine . interrupt . set_status ( old_status ) ;
2023-03-09 12:08:33 +01:00
}
2023-03-22 17:17:53 +01:00
/// Save the CPU state of a user program on a context switch.
2023-05-24 22:00:16 +02:00
///
/// Save PC and registers
2023-04-20 11:31:25 +02:00
pub fn thread_save_processor_state ( & mut self , machine : & mut Machine , thread : ThreadRef ) {
2023-03-16 14:52:53 +01:00
let mut t = thread . borrow_mut ( ) ;
2023-03-15 15:20:20 +01:00
for i in 0 .. NUM_INT_REGS {
2023-03-15 17:57:53 +01:00
t . thread_context . int_registers [ i ] = machine . read_int_register ( i ) ;
2023-03-15 15:20:20 +01:00
}
for i in 0 .. NUM_FP_REGS {
2023-03-15 17:57:53 +01:00
t . thread_context . float_registers [ i ] = machine . read_fp_register ( i ) ;
2023-03-09 12:08:33 +01:00
}
2023-03-27 22:20:29 +02:00
t . thread_context . pc = machine . pc ;
2023-03-09 12:08:33 +01:00
}
2023-03-22 17:17:53 +01:00
/// Restore the CPU state of a user program on a context switch.
2023-05-24 22:00:16 +02:00
///
/// Restore PC and registers
2023-04-20 11:31:25 +02:00
pub fn thread_restore_processor_state ( & self , machine : & mut Machine , thread : ThreadRef ) {
2023-03-15 15:20:20 +01:00
let t : Ref < _ > = thread . borrow ( ) ;
for i in 0 .. NUM_INT_REGS {
machine . write_int_register ( i , t . thread_context . int_registers [ i ] ) ;
2023-03-09 12:08:33 +01:00
}
2023-03-27 22:20:29 +02:00
machine . pc = t . thread_context . pc ;
2023-03-09 12:08:33 +01:00
}
2023-04-12 15:22:22 +02:00
/// Decrement the value, and wait if it becomes < 0. Checking the
2023-04-20 00:05:37 +02:00
/// value and decrementing must be done atomically, so we
/// need to disable interrupts before checking the value.
2023-04-12 15:22:22 +02:00
///
2023-04-20 00:05:37 +02:00
/// Note that thread_manager::thread_sleep assumes that interrupts are disabled
/// when it is called.
2023-04-12 15:22:22 +02:00
///
/// ### Parameters
/// - *id_sema* id of the semaphore, stored in [`ObjAddr`], id given by user program thought exceptions
/// - *machine* Current state of the machine
2023-04-12 14:49:08 +02:00
pub fn sem_p ( & mut self , id_sema : i32 , machine : & mut Machine ) -> Result < MachineOk , MachineError > {
2023-04-19 23:24:51 +02:00
let old_status = machine . interrupt . set_status ( InterruptStatus ::InterruptOff ) ;
let thread = match self . get_g_current_thread ( ) {
Some ( thread ) = > Rc ::clone ( thread ) ,
None = > Err ( " sem_p error: current thread should not be None " ) ?
} ;
let sema = match self . get_obj_addrs ( ) . search_semaphore ( id_sema ) {
Some ( sema ) = > sema ,
None = > Err ( " sem_p error: cannot find semaphore " ) ?
} ;
sema . counter - = 1 ;
if sema . counter < 0 {
sema . waiting_queue . push ( thread . clone ( ) ) ;
self . thread_sleep ( machine , thread ) ;
}
machine . interrupt . set_status ( old_status ) ;
Ok ( MachineOk ::Ok )
2023-04-12 14:49:08 +02:00
}
2023-04-12 15:22:22 +02:00
/// Increment semaphore value, waking up a waiting thread if any.
/// As with P(), this operation must be atomic, so we need to disable
/// interrupts.
///
/// scheduler::ready_to_run() assumes that interrupts
/// are disabled when it is called.
///
/// ### Parameters
/// - *id_sema* id of the semaphore, stored in [`ObjAddr`], id given by user program thought exceptions
/// - **machine** the machine where the threads are executed
2023-04-12 14:49:08 +02:00
pub fn sem_v ( & mut self , id_sema : i32 , machine : & mut Machine ) -> Result < MachineOk , MachineError > {
2023-04-19 23:24:51 +02:00
let sema = match self . get_obj_addrs ( ) . search_semaphore ( id_sema ) {
Some ( sema ) = > sema ,
None = > Err ( " sem_v error: cannot find semaphore " ) ?
} ;
let old_status = machine . interrupt . set_status ( InterruptStatus ::InterruptOff ) ;
sema . counter + = 1 ;
2023-04-19 23:38:58 +02:00
if let Some ( thread ) = sema . waiting_queue . pop ( ) {
self . ready_to_run ( thread )
2023-04-12 14:49:08 +02:00
}
2023-04-19 23:24:51 +02:00
machine . interrupt . set_status ( old_status ) ;
Ok ( MachineOk ::Ok )
2023-04-12 14:49:08 +02:00
}
2023-04-13 00:17:34 +02:00
/// Wait until the lock become free. Checking the
2023-04-20 00:05:37 +02:00
/// state of the lock (free or busy) and modify it must be done
/// atomically, so we need to disable interrupts before checking
/// the value of free.
2023-04-13 00:17:34 +02:00
///
2023-04-20 00:05:37 +02:00
/// Note that thread_manager::thread_seep assumes that interrupts are disabled
/// when it is called.
2023-04-13 00:17:34 +02:00
///
/// ### Parameters
/// - **id** id of the lock, stored in [`ObjAddr`], id given by user program thought exceptions
/// - **machine** the machine where the threads are executed
pub fn lock_acquire ( & mut self , id : i32 , machine : & mut Machine ) -> Result < MachineOk , MachineError > {
2023-04-19 23:13:27 +02:00
let current_thread = match self . get_g_current_thread ( ) {
Some ( thread ) = > Rc ::clone ( thread ) ,
None = > Err ( " lock_acquire error: current_thread should not be None. " ) ?
} ;
let old_status = machine . interrupt . set_status ( InterruptStatus ::InterruptOff ) ;
if let Some ( lock ) = self . get_obj_addrs ( ) . search_lock ( id ) {
if lock . free {
lock . free = false ;
lock . owner = Some ( current_thread )
} else {
lock . waiting_queue . push ( current_thread . clone ( ) ) ;
self . thread_sleep ( machine , current_thread ) ;
}
} else {
Err ( " lock_acquire error: cannot find Lock. " ) ?
2023-04-13 00:17:34 +02:00
}
2023-04-19 23:13:27 +02:00
machine . interrupt . set_status ( old_status ) ;
Ok ( MachineOk ::Ok )
2023-04-13 00:17:34 +02:00
}
2023-05-24 22:00:16 +02:00
/// 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
2023-04-18 12:13:56 +02:00
pub fn lock_release ( & mut self , id : i32 , machine : & mut Machine ) -> Result < MachineOk , MachineError > {
2023-05-04 22:58:13 +02:00
let old_status = machine . interrupt . set_status ( InterruptStatus ::InterruptOff ) ;
2023-04-19 21:30:28 +02:00
let current_thread = match self . get_g_current_thread ( ) {
2023-04-20 00:05:37 +02:00
Some ( thread ) = > Rc ::clone ( thread ) ,
2023-04-19 21:30:28 +02:00
None = > Err ( String ::from ( " lock_release error: current_thread should not be None. " ) ) ?
} ;
2023-05-04 22:58:13 +02:00
let mut lock = match self . get_obj_addrs ( ) . search_lock ( id ) {
2023-04-19 21:30:28 +02:00
Some ( lock ) = > lock ,
None = > Err ( String ::from ( " lock_release error: cannot find lock. " ) ) ?
} ;
if let Some ( lock_owner ) = & lock . owner {
2023-05-04 22:58:13 +02:00
if current_thread . eq ( lock_owner ) { // is_held_by_current_thread
match lock . waiting_queue . pop ( ) {
Some ( th ) = > {
lock . owner = Some ( Rc ::clone ( & th ) ) ;
self . ready_to_run ( Rc ::clone ( & th ) ) ;
} ,
None = > {
lock . free = true ;
lock . owner = None ;
}
}
2023-04-19 21:30:28 +02:00
}
} ;
2023-05-04 22:58:13 +02:00
// self.get_obj_addrs().update_lock(id, lock);
2023-05-04 23:44:10 +02:00
2023-04-19 21:30:28 +02:00
machine . interrupt . set_status ( old_status ) ;
Ok ( MachineOk ::Ok )
2023-04-13 14:45:57 +02:00
}
2023-05-24 22:00:16 +02:00
/// Return currently running thread
2023-04-20 11:31:25 +02:00
pub fn get_g_current_thread ( & mut self ) -> & Option < ThreadRef > {
2023-03-16 14:52:53 +01:00
& self . g_current_thread
2023-03-08 21:10:51 +01:00
}
2023-05-24 22:00:16 +02:00
/// Return list of alive threads
2023-04-20 11:31:25 +02:00
pub fn get_g_alive ( & mut self ) -> & mut List < ThreadRef > {
2023-03-08 21:10:51 +01:00
& mut self . g_alive
}
/// Set currently running thread
2023-04-20 11:31:25 +02:00
pub fn set_g_current_thread ( & mut self , thread : Option < ThreadRef > ) {
2023-03-08 21:10:51 +01:00
self . g_current_thread = thread
}
2023-04-19 23:38:58 +02:00
/// Returns a mutable reference to the ObjAddr field of this thread_manager
2023-04-12 14:49:08 +02:00
pub fn get_obj_addrs ( & mut self ) -> & mut ObjAddr {
& mut self . obj_addrs
}
2023-04-19 23:38:58 +02:00
/// Prints debug messages if self.debug is set to true.
2023-04-13 02:05:21 +02:00
fn debug ( & self , message : String ) {
if self . debug {
println! ( " {} " , message ) ;
}
}
2023-05-09 17:01:52 +02:00
pub fn get_sp_max ( & self ) -> u64 {
self . sp_max
}
pub fn set_sp_max ( & mut self , sp_max : u64 ) {
self . sp_max = sp_max ;
}
2023-03-22 15:52:48 +01:00
}
#[ cfg(test) ]
mod test {
2023-03-22 18:30:31 +01:00
use std ::{ rc ::Rc , cell ::RefCell } ;
2023-04-19 18:09:08 +02:00
use crate ::{ simulator ::{ machine ::Machine , loader } , kernel ::{ system ::System , thread ::Thread , process ::Process , thread_manager ::ThreadManager , synch ::Semaphore } , utility ::cfg ::get_debug_configuration } ;
2023-04-19 15:52:31 +02:00
use crate ::kernel ::synch ::Lock ;
2023-03-22 18:30:31 +01:00
#[ test ]
fn test_thread_context ( ) {
2023-04-19 18:09:08 +02:00
let mut machine = Machine ::new ( true , get_debug_configuration ( ) ) ;
2023-04-05 16:47:43 +02:00
2023-04-05 17:07:58 +02:00
let ( loader , ptr ) = loader ::Loader ::new ( " ./target/guac/halt.guac " , & mut machine , 0 ) . expect ( " IO Error " ) ;
2023-04-01 00:14:09 +02:00
let start_pc = loader . elf_header . entrypoint ;
2023-04-13 02:05:21 +02:00
let system = & mut System ::new ( true ) ;
2023-03-28 21:26:58 +02:00
2023-03-22 18:30:31 +01:00
let thread1 = Thread ::new ( " th1 " ) ;
let thread1 = Rc ::new ( RefCell ::new ( thread1 ) ) ;
system . get_thread_manager ( ) . get_g_alive ( ) . push ( Rc ::clone ( & thread1 ) ) ;
2023-03-28 21:26:58 +02:00
2023-04-04 22:01:49 +02:00
let owner1 = Process { num_thread : 0 } ;
2023-04-11 17:47:36 +02:00
let owner1 = Rc ::new ( RefCell ::new ( owner1 ) ) ;
2023-04-23 15:11:06 +02:00
system . get_thread_manager ( ) . start_thread ( Rc ::clone ( & thread1 ) , owner1 , loader . elf_header . entrypoint , ptr + machine . page_size , - 1 ) ;
2023-03-28 19:47:42 +02:00
debug_assert_eq! ( thread1 . borrow_mut ( ) . thread_context . pc , start_pc ) ;
2023-04-04 22:01:49 +02:00
debug_assert! ( system . get_thread_manager ( ) . get_g_alive ( ) . contains ( & Rc ::clone ( & thread1 ) ) ) ;
2023-03-28 21:26:58 +02:00
2023-03-22 18:30:31 +01:00
let to_run = system . get_thread_manager ( ) . find_next_to_run ( ) . unwrap ( ) ;
2023-04-05 12:01:31 +02:00
debug_assert_eq! ( to_run , Rc ::clone ( & thread1 ) ) ;
2023-03-28 21:26:58 +02:00
2023-03-22 18:30:31 +01:00
system . get_thread_manager ( ) . switch_to ( & mut machine , Rc ::clone ( & to_run ) ) ;
2023-04-05 12:01:31 +02:00
debug_assert_eq! ( system . get_thread_manager ( ) . g_current_thread , Option ::Some ( Rc ::clone ( & thread1 ) ) ) ;
debug_assert_eq! ( machine . pc , loader . elf_header . entrypoint ) ;
2023-03-28 21:26:58 +02:00
2023-04-05 12:01:31 +02:00
machine . run ( system ) ;
2023-03-22 18:30:31 +01:00
}
2023-03-22 15:52:48 +01:00
2023-04-19 15:52:31 +02:00
#[ test ]
2023-04-20 15:20:28 +02:00
fn test_lock_single ( ) {
2023-04-19 18:09:08 +02:00
let mut machine = Machine ::new ( true , get_debug_configuration ( ) ) ;
2023-04-19 15:52:31 +02:00
let mut thread_manager = ThreadManager ::new ( true ) ;
let lock = Lock ::new ( ) ;
let lock_id = thread_manager . get_obj_addrs ( ) . add_lock ( lock ) ;
let thread = Rc ::new ( RefCell ::new ( Thread ::new ( " test_lock " ) ) ) ;
2023-04-20 14:43:15 +02:00
let thread_test = thread . clone ( ) ;
2023-04-19 15:52:31 +02:00
thread_manager . ready_to_run ( Rc ::clone ( & thread ) ) ;
thread_manager . set_g_current_thread ( Some ( thread ) ) ;
thread_manager . lock_acquire ( lock_id , & mut machine ) . expect ( " lock acquire return an error: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-04-20 14:43:15 +02:00
assert_eq! ( lock . owner , Some ( thread_test ) ) ;
2023-04-19 15:52:31 +02:00
assert! ( ! lock . free ) ;
assert! ( lock . waiting_queue . is_empty ( ) ) ;
}
thread_manager . lock_release ( lock_id , & mut machine ) . expect ( " lock release return an error: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-04-20 14:43:15 +02:00
assert_eq! ( lock . owner , None ) ;
2023-04-20 14:50:44 +02:00
assert! ( lock . free ) ;
2023-04-19 15:52:31 +02:00
assert! ( lock . waiting_queue . is_empty ( ) ) ;
}
}
2023-04-20 15:20:28 +02:00
#[ test ]
fn test_lock_multiple ( ) {
let mut machine = Machine ::new ( true , get_debug_configuration ( ) ) ;
let mut thread_manager = ThreadManager ::new ( true ) ;
let lock = Lock ::new ( ) ;
let lock_id = thread_manager . get_obj_addrs ( ) . add_lock ( lock ) ;
let thread_1 = Rc ::new ( RefCell ::new ( Thread ::new ( " test_lock_1 " ) ) ) ;
let thread_2 = Rc ::new ( RefCell ::new ( Thread ::new ( " test_lock_2 " ) ) ) ;
2023-05-04 23:44:10 +02:00
thread_manager . ready_to_run ( thread_1 . clone ( ) ) ;
thread_manager . ready_to_run ( thread_2 . clone ( ) ) ;
thread_manager . set_g_current_thread ( Some ( thread_1 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
thread_manager . lock_acquire ( lock_id , & mut machine ) . expect ( " lock acquire return an error at first iteration: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-05-04 23:44:10 +02:00
assert_eq! ( lock . owner , Some ( thread_1 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
assert! ( ! lock . free ) ;
assert! ( lock . waiting_queue . is_empty ( ) ) ;
}
2023-05-04 23:44:10 +02:00
thread_manager . set_g_current_thread ( Some ( thread_2 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
thread_manager . lock_acquire ( lock_id , & mut machine ) . expect ( " lock acquire return an error at second iteration: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-05-04 23:44:10 +02:00
assert_eq! ( lock . owner , Some ( thread_1 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
assert! ( ! lock . free ) ;
assert_eq! ( lock . waiting_queue . iter ( ) . count ( ) , 1 ) ;
}
2023-05-04 23:44:10 +02:00
2023-04-20 15:20:28 +02:00
thread_manager . lock_release ( lock_id , & mut machine ) . expect ( " lock release return an error at first iteration: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-05-04 23:44:10 +02:00
assert_eq! ( lock . owner , Some ( thread_2 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
assert! ( ! lock . free ) ;
assert! ( lock . waiting_queue . is_empty ( ) ) ;
}
2023-05-04 23:44:10 +02:00
thread_manager . set_g_current_thread ( Some ( thread_2 . clone ( ) ) ) ;
2023-04-20 15:20:28 +02:00
thread_manager . lock_release ( lock_id , & mut machine ) . expect ( " lock release return an error at second iteration: " ) ;
{
let lock = thread_manager . get_obj_addrs ( ) . search_lock ( lock_id ) . unwrap ( ) ;
2023-04-20 15:34:49 +02:00
assert! ( lock . waiting_queue . is_empty ( ) ) ;
2023-04-20 15:20:28 +02:00
assert_eq! ( lock . owner , None ) ;
assert! ( lock . free ) ;
}
}
2023-04-12 15:22:22 +02:00
#[ test ]
fn test_semaphore_single ( ) {
// Init
2023-04-19 18:09:08 +02:00
let mut machine = Machine ::new ( true , get_debug_configuration ( ) ) ;
2023-04-13 02:05:21 +02:00
let mut thread_manager = ThreadManager ::new ( true ) ;
2023-04-12 15:22:22 +02:00
let semaphore = Semaphore ::new ( 1 ) ;
let sema_id = thread_manager . get_obj_addrs ( ) . add_semaphore ( semaphore ) ;
let thread = Rc ::new ( RefCell ::new ( Thread ::new ( " test_semaphore " ) ) ) ;
thread_manager . ready_to_run ( Rc ::clone ( & thread ) ) ;
thread_manager . set_g_current_thread ( Some ( thread ) ) ;
// P
thread_manager . sem_p ( sema_id , & mut machine ) . expect ( " semaphore P return an error: " ) ;
{
let semaphore = thread_manager . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 0 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
// V
thread_manager . sem_v ( sema_id , & mut machine ) . expect ( " semaphore V return an error: " ) ;
{
let semaphore = thread_manager . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 1 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
}
#[ test ]
fn test_semaphore_multiple ( ) {
// Init
2023-04-13 02:05:21 +02:00
let mut tm = ThreadManager ::new ( true ) ;
2023-04-19 18:09:08 +02:00
let mut machine = Machine ::new ( true , get_debug_configuration ( ) ) ;
2023-04-12 15:32:46 +02:00
let semaphore = Semaphore ::new ( 2 ) ;
2023-04-12 15:22:22 +02:00
let sema_id = tm . get_obj_addrs ( ) . add_semaphore ( semaphore ) ;
let thread1 = Rc ::new ( RefCell ::new ( Thread ::new ( " test_semaphore_1 " ) ) ) ;
let thread2 = Rc ::new ( RefCell ::new ( Thread ::new ( " test_semaphore_2 " ) ) ) ;
let thread3 = Rc ::new ( RefCell ::new ( Thread ::new ( " test_semaphore_3 " ) ) ) ;
// let mut borrow_tm = tm.borrow_mut();
// let scheduler = &mut tm.g_scheduler;
tm . ready_to_run ( Rc ::clone ( & thread1 ) ) ;
tm . ready_to_run ( Rc ::clone ( & thread2 ) ) ;
tm . ready_to_run ( Rc ::clone ( & thread3 ) ) ;
// P
tm . set_g_current_thread ( Some ( Rc ::clone ( & thread1 ) ) ) ;
tm . sem_p ( sema_id , & mut machine ) . expect ( " semaphore P return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 1 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
tm . set_g_current_thread ( Some ( Rc ::clone ( & thread2 ) ) ) ;
tm . sem_p ( sema_id , & mut machine ) . expect ( " semaphore P return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 0 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
tm . set_g_current_thread ( Some ( Rc ::clone ( & thread3 ) ) ) ;
tm . sem_p ( sema_id , & mut machine ) . expect ( " semaphore P return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , - 1 ) ;
assert! ( semaphore . waiting_queue . iter ( ) . count ( ) = = 1 ) ;
}
// V
tm . sem_v ( sema_id , & mut machine ) . expect ( " semaphore V return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 0 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
tm . sem_v ( sema_id , & mut machine ) . expect ( " semaphore V return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 1 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
tm . sem_v ( sema_id , & mut machine ) . expect ( " semaphore V return an error: " ) ;
{
let semaphore = tm . get_obj_addrs ( ) . search_semaphore ( sema_id ) . unwrap ( ) ;
assert_eq! ( semaphore . counter , 2 ) ;
assert! ( semaphore . waiting_queue . is_empty ( ) ) ;
}
}
2023-03-08 21:10:51 +01:00
}