diff --git a/src/kernel/exception.rs b/src/kernel/exception.rs index f90f271..45fb0c1 100644 --- a/src/kernel/exception.rs +++ b/src/kernel/exception.rs @@ -278,7 +278,7 @@ fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result usize { let mut c = 1; while c != 0 { c = machine.read_memory(1, addr + i); - i +=1; + i += 1; } - println!("addr: {:x}, i: {}", addr, i + 1); i + 1 } @@ -334,7 +333,6 @@ fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec dest.push(c as char); i += 1; } - dest.push('\0'); dest } diff --git a/src/kernel/thread.rs b/src/kernel/thread.rs index 838a5de..d0bdc3e 100644 --- a/src/kernel/thread.rs +++ b/src/kernel/thread.rs @@ -1,7 +1,7 @@ use std::{rc::Rc, cell::RefCell}; -use super::process::Process; -use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}}; +use super::{process::Process, thread_manager::ThreadRef}; +use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::list::List}; const STACK_FENCEPOST: u32 = 0xdeadbeef; @@ -26,7 +26,7 @@ pub struct Thread { name: String, pub process: Option>>, pub thread_context: ThreadContext, - pub stack_pointer: i32, + pub join_thread: List, } impl Thread { @@ -40,9 +40,9 @@ impl Thread { thread_context: ThreadContext { int_registers: [0; NUM_INT_REGS], float_registers: [0f32; NUM_FP_REGS], - pc: 0 + pc: 0, }, - stack_pointer: 0, + join_thread: List::default(), } } @@ -94,7 +94,6 @@ mod test { float_registers: [0f32; NUM_FP_REGS], pc: 0 }; - x.stack_pointer = 0; x } }; } diff --git a/src/kernel/thread_manager.rs b/src/kernel/thread_manager.rs index c6cd396..82faf87 100644 --- a/src/kernel/thread_manager.rs +++ b/src/kernel/thread_manager.rs @@ -102,7 +102,7 @@ use crate::{ }; /// Using this type alias to simplify struct and method definitions -type ThreadRef = Rc>; +pub type ThreadRef = Rc>; /// # Thread manager /// @@ -186,6 +186,8 @@ impl ThreadManager { /// Start a thread, attaching it to a process 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())); + let mut thread_m = thread.borrow_mut(); assert_eq!(thread_m.process, Option::None); thread_m.process = Option::Some(Rc::clone(&owner)); @@ -199,23 +201,29 @@ impl ThreadManager { /// Wait for another thread to finish its execution pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) { let waiting_for = Rc::clone(&waiting_for); - while self.get_g_alive().contains(&waiting_for) { - self.debug(format!("Joining \"{}\" to \"{}\"", waiter.borrow().get_name(), waiting_for.borrow().get_name())); - self.thread_yield(machine, Rc::clone(&waiter)); + 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); } } /// Relinquish the CPU if any other thread is runnable. /// /// Cannot use yield as a function name -> reserved name in rust - pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef) { + /// + /// ## Parameters + /// + /// **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); - self.debug(format!("Yeilding thread: {}", thread.borrow().get_name())); + self.debug(format!("Yeilding thread: \"{}\"", thread.borrow().get_name())); debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread()); let next_thread = self.find_next_to_run(); if let Some(next_thread) = next_thread { - self.ready_to_run(thread); + if is_ready { + self.ready_to_run(thread); + } self.switch_to(machine, next_thread); } machine.interrupt.set_status(old_status); @@ -226,6 +234,7 @@ impl ThreadManager { debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread); debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff); + self.debug(format!("Sleeping thread {}", thread.borrow().get_name())); let mut next_thread = self.find_next_to_run(); while next_thread.is_none() { eprintln!("Nobody to run => idle"); @@ -240,8 +249,11 @@ impl ThreadManager { pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) { let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff); assert!(self.g_alive.remove(Rc::clone(&thread))); - self.debug(format!("Sleeping thread {}", thread.borrow().get_name())); + self.debug(format!("Finishing thread {}", thread.borrow().get_name())); // g_objets_addrs->removeObject(self.thread) // a ajouté plus tard + for (_, el) in thread.borrow().join_thread.iter().enumerate() { + self.ready_to_run(Rc::clone(&el)); + } self.thread_sleep(machine, Rc::clone(&thread)); machine.interrupt.set_status(old_status); } @@ -432,7 +444,7 @@ mod test { let owner1 = Process { num_thread: 0 }; let owner1 = Rc::new(RefCell::new(owner1)); - system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr, -1); + system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -1); debug_assert_eq!(thread1.borrow_mut().thread_context.pc, start_pc); debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1))); diff --git a/src/main.rs b/src/main.rs index 9de0c09..4b701dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ use simulator::{machine::Machine, loader}; use clap::Parser; -use utility::cfg::{get_debug_configuration, read_settings}; +use utility::cfg::read_settings; #[derive(Parser, Debug)] #[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System) @@ -30,9 +30,13 @@ Burritos is an educational operating system written in Rust running on RISC-V emulator.", long_about = None)] /// Launch argument parser struct Args { - /// Enable debug mode - #[arg(short, long)] - debug: bool, + /// Enable debug mode. + /// 0 to disable debug, + /// 1 to enable machine debug, + /// 2 to enable system debug, + /// 3 to enable all debug + #[arg(short, long, value_parser = clap::value_parser!(u8).range(0..=3))] + debug: u8, /// Path to the executable binary file to execute #[arg(short = 'x', long, value_name = "PATH")] executable: String @@ -41,10 +45,10 @@ struct Args { fn main() { let args = Args::parse(); - let mut machine = Machine::new(args.debug, read_settings().unwrap()); + let mut machine = Machine::new(args.debug & 1 != 0, read_settings().unwrap()); let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program"); - let mut system = System::new(args.debug); + let mut system = System::new(args.debug & 2 != 0); let thread_exec = Thread::new(args.executable.as_str()); let thread_exec = Rc::new(RefCell::new(thread_exec)); @@ -52,7 +56,7 @@ fn main() { 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); + system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -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)); diff --git a/src/simulator/machine.rs b/src/simulator/machine.rs index 040da76..fae3a68 100644 --- a/src/simulator/machine.rs +++ b/src/simulator/machine.rs @@ -95,7 +95,7 @@ pub struct Machine { // futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize; //creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg num_phy_page: u64, - page_size: u64, + pub page_size: u64, /// Current machine status pub status: MachineStatus } diff --git a/src/utility/list.rs b/src/utility/list.rs index 7a90d0a..0334261 100644 --- a/src/utility/list.rs +++ b/src/utility/list.rs @@ -9,11 +9,10 @@ use std::ptr; /// These methods wrap unsafe instructions because it doesn't respect borrow rules per example /// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.) /// or memory leak -#[derive(PartialEq)] -#[derive(Clone)] +#[derive(PartialEq, Clone, Debug)] pub struct List { head: Link, - tail: *mut Node, + tail: Link, } type Link = *mut Node; diff --git a/test/syscall_tests/Makefile b/test/syscall_tests/Makefile index 8388949..79041ed 100644 --- a/test/syscall_tests/Makefile +++ b/test/syscall_tests/Makefile @@ -1,4 +1,4 @@ -PROGRAMS = halt.guac prints.guac producteur_consommateur.guac +PROGRAMS = halt.guac prints.guac producteur_consommateur.guac join.guac TOPDIR = ../.. include $(TOPDIR)/Makefile.rules diff --git a/test/syscall_tests/join.c b/test/syscall_tests/join.c new file mode 100644 index 0000000..8d26596 --- /dev/null +++ b/test/syscall_tests/join.c @@ -0,0 +1,36 @@ +#include "userlib/syscall.h" +#include "userlib/libnachos.h" + + +const int N = 3; +int iplein = 0; +int ivide = 0; +int tab[3]; +SemId svide; +SemId splein; + +void th1(); + +void th2(); + +int main() { + ThreadId th1 = threadCreate("th1", th1); + ThreadId th2 = threadCreate("th2", th2); + Join(th1); + Join(th2); + return 0; +} + +void th1() { + for(int i = 0; i < 10; i++) + { + n_printf("Hello from th1\n"); + } +} + +void th2() { + for(int i = 0; i < 10; i++) + { + n_printf("Hello from th2\n"); + } +}