From 780ed4b4619878259dc2412104754a4d3b7e06e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Autin?= Date: Thu, 20 Apr 2023 00:05:37 +0200 Subject: [PATCH] :memo: Added module documentation for thread_manager --- src/kernel/thread_manager.rs | 88 ++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/src/kernel/thread_manager.rs b/src/kernel/thread_manager.rs index 9f64b84..514d495 100644 --- a/src/kernel/thread_manager.rs +++ b/src/kernel/thread_manager.rs @@ -1,3 +1,71 @@ +//! # Thread manager +//! +//! 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 +//! all existing threads and synchronization objects, such as `Locks`, `Semaphores` and +//! `Conditions`. +//! +//! ## Purpose +//! +//! `ThreadManager` holds the state of the system processes using the following subcomponents: +//! +//! ### Two lists of threads +//! +//! - **ready_list**: The list of threads ready to be executed +//! - **g_alive**: The list of currently executing threads +//! +//! 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. +//! +//! ### A list of synchronization objects +//! +//! 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 +//! 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. +//! +//! 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 +//! through methods such as `ThreadManager::sem_p`. +//! +//! ## Usage +//! +//! `ThreadManager` is thought as a subcomponent of the `System` struct. Instanciating +//! `System` will automatically instanciate a `ThreadManager` +//! +//! Manually loading a Thread into ThreadManager to execute a program with BurritOS could look like +//! this: +//! +//! ``` +//! fn load_thread_manually(args: ...) { +//! let mut system = System::new(args.debug); +//! +//! 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)); +//! +//! 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); +//! +//! let to_run = system.get_thread_manager().find_next_to_run().unwrap(); +//! system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run)); +//! +//! machine.run(&mut system); +//! } +//! ``` +//! +//! ## Imports +//! +//! 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 +//! to keep this module concise. + use std::{ rc::Rc, cell::{ @@ -190,11 +258,11 @@ impl ThreadManager { } /// Decrement the value, and wait if it becomes < 0. Checking the - /// value and decrementing must be done atomically, so we - /// need to disable interrupts before checking the value. + /// value and decrementing must be done atomically, so we + /// need to disable interrupts before checking the value. /// - /// Note that thread_manager::thread_sleep assumes that interrupts are disabled - /// when it is called. + /// Note that thread_manager::thread_sleep 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 @@ -244,12 +312,12 @@ impl ThreadManager { /// Wait until the lock become free. Checking the - /// 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. + /// 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. /// - /// Note that thread_manager::thread_seep assumes that interrupts are disabled - /// when it is called. + /// Note that thread_manager::thread_seep assumes that interrupts are disabled + /// when it is called. /// /// ### Parameters /// - **id** id of the lock, stored in [`ObjAddr`], id given by user program thought exceptions @@ -278,7 +346,7 @@ impl ThreadManager { /// Wake up a waiter if necessary, or release it if no thread is waiting. pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result { let current_thread = match self.get_g_current_thread() { - Some(thread) => Rc::clone(&thread), + Some(thread) => Rc::clone(thread), None => Err(String::from("lock_release error: current_thread should not be None."))? }; let mut lock = match self.get_obj_addrs().search_lock(id).cloned() {