Signed-off-by: Rémi Rativel <remi.rativel@etudiant.univ-rennes1.fr>
This commit is contained in:
Rémi Rativel 2023-04-20 14:43:15 +02:00
parent bb1d2383bb
commit c33df28307

View File

@ -1,67 +1,67 @@
//! # Thread manager //! # Thread manager
//! //!
//! This module describes the data structure and the methods used for thread scheduling //! This module describes the data structure and the methods used for thread scheduling
//! in the BurritOS operating system. A struct named `ThreadManager` holds the list of //! in the BurritOS operating system. A struct named `ThreadManager` holds the list of
//! all existing threads and synchronization objects, such as `Locks`, `Semaphores` and //! all existing threads and synchronization objects, such as `Locks`, `Semaphores` and
//! `Conditions`. //! `Conditions`.
//! //!
//! ## Purpose //! ## Purpose
//! //!
//! `ThreadManager` holds the state of the system processes using the following subcomponents: //! `ThreadManager` holds the state of the system processes using the following subcomponents:
//! //!
//! ### Two lists of threads //! ### Two lists of threads
//! //!
//! - **ready_list**: The list of threads ready to be executed //! - **ready_list**: The list of threads ready to be executed
//! - **g_alive**: The list of currently executing threads //! - **g_alive**: The list of currently executing threads
//! //!
//! The difference between the two above lists lies in the state of the threads in question. //! 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 //! 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 //! 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 //! 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. //! threads in case the currently running thread finishes or gets rescheduled.
//! //!
//! ### A list of synchronization objects //! ### A list of synchronization objects
//! //!
//! Locks, Semaphores and Conditions allow resource sharing among running threads. Since resources //! 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 //! can only be accessed by a single thread at a time, we need data structures to signal other
//! threads that a resource may be busy or unavailable; say for example that thread **A** wants to //! 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 //! 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 //! 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. //! avoid such issues. Thread **A** will have to wait for **B** to finish reading the file.
//! //!
//! These synchronization objects are held in an instance of the ObjAddr structure held by //! 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 //! ThreadManager. Their state is mutated depending on the actions of the currently running thread
//! through methods such as `ThreadManager::sem_p`. //! through methods such as `ThreadManager::sem_p`.
//! //!
//! ## Usage //! ## Usage
//! //!
//! `ThreadManager` is thought as a subcomponent of the `System` struct. Instanciating //! `ThreadManager` is thought as a subcomponent of the `System` struct. Instanciating
//! `System` will automatically instanciate a `ThreadManager` //! `System` will automatically instanciate a `ThreadManager`
//! //!
//! Manually loading a Thread into ThreadManager to execute a program with BurritOS could look like //! Manually loading a Thread into ThreadManager to execute a program with BurritOS could look like
//! this: //! this:
//! //!
//! ``` //! ```
//! fn load_thread_manually(args: ...) { //! fn load_thread_manually(args: ...) {
//! let mut system = System::new(args.debug); //! let mut system = System::new(args.debug);
//! //!
//! let thread_exec = Thread::new(args.executable.as_str()); //! let thread_exec = Thread::new(args.executable.as_str());
//! let thread_exec = Rc::new(RefCell::new(thread_exec)); //! let thread_exec = Rc::new(RefCell::new(thread_exec));
//! system.get_thread_manager().get_g_alive().push(Rc::clone(&thread_exec)); //! system.get_thread_manager().get_g_alive().push(Rc::clone(&thread_exec));
//! //!
//! let owner1 = Process { num_thread: 0 }; //! let owner1 = Process { num_thread: 0 };
//! let owner1 = Rc::new(RefCell::new(owner1)); //! let owner1 = Rc::new(RefCell::new(owner1));
//! system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr, -1); //! system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr, -1);
//! //!
//! let to_run = system.get_thread_manager().find_next_to_run().unwrap(); //! let to_run = system.get_thread_manager().find_next_to_run().unwrap();
//! system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run)); //! system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
//! //!
//! machine.run(&mut system); //! machine.run(&mut system);
//! } //! }
//! ``` //! ```
//! //!
//! ## Imports //! ## Imports
//! //!
//! The `List` and `ObjAddr` submodules used in this module are defined in the utility //! 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 //! module. The source code of ObjAddr has been decoupled from thread_manager in an effort
//! to keep this module concise. //! to keep this module concise.
@ -448,12 +448,14 @@ mod test {
let lock = Lock::new(); let lock = Lock::new();
let lock_id = thread_manager.get_obj_addrs().add_lock(lock); let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
let thread = Rc::new(RefCell::new(Thread::new("test_lock"))); let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
let thread_test = thread.clone();
thread_manager.ready_to_run(Rc::clone(&thread)); thread_manager.ready_to_run(Rc::clone(&thread));
thread_manager.set_g_current_thread(Some(thread)); thread_manager.set_g_current_thread(Some(thread));
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error: "); 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(); let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert_eq!(lock.owner,Some(thread_test));
assert!(!lock.free); assert!(!lock.free);
assert!(lock.waiting_queue.is_empty()); assert!(lock.waiting_queue.is_empty());
} }
@ -461,6 +463,7 @@ mod test {
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error: "); 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(); let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
assert_eq!(lock.owner, None);
assert!(!lock.free); assert!(!lock.free);
assert!(lock.waiting_queue.is_empty()); assert!(lock.waiting_queue.is_empty());
} }