Compare commits
15 Commits
demo_files
...
clippy_fix
Author | SHA1 | Date | |
---|---|---|---|
9b87a0cd83 | |||
d35314bead | |||
7b7d48c775 | |||
9dec9b041a | |||
9bd0ef02aa | |||
c6f5818059 | |||
31f1e760e9 | |||
f6195a9da0 | |||
5393c6e3f2 | |||
ff921117f7 | |||
ce4c7230f9 | |||
33cbe77175 | |||
052b950ca0 | |||
f06f14354a | |||
8732a6f0b7 |
12
build.rs
Normal file
12
build.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//! Build script for BurritOS.
|
||||||
|
//!
|
||||||
|
//! Moves files from the assets folder to the target directory
|
||||||
|
//! and runs `make all`.
|
||||||
|
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut make_all = Command::new("make");
|
||||||
|
make_all.arg("all");
|
||||||
|
println!("{:?}", make_all.output().unwrap());
|
||||||
|
}
|
@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc};
|
|||||||
use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}};
|
use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}};
|
||||||
use crate::kernel::synch::{Lock, Semaphore};
|
use crate::kernel::synch::{Lock, Semaphore};
|
||||||
|
|
||||||
use super::{system::{System, self}, thread::Thread};
|
use super::{system::{System}, thread::Thread};
|
||||||
|
|
||||||
/// The halt system call. Stops Burritos.
|
/// The halt system call. Stops Burritos.
|
||||||
pub const SC_SHUTDOWN: u8 = 0;
|
pub const SC_SHUTDOWN: u8 = 0;
|
||||||
@ -239,7 +239,7 @@ fn sc_sem_create(machine: &mut Machine, system: &mut System) -> Result<MachineOk
|
|||||||
let size = get_length_param(addr_name, machine);
|
let size = get_length_param(addr_name, machine);
|
||||||
let _name = get_string_param(addr_name, size, machine);
|
let _name = get_string_param(addr_name, size, machine);
|
||||||
match initial_count < 0 {
|
match initial_count < 0 {
|
||||||
true => Err(format!("Initial_count < 0"))?,
|
true => Err("Initial_count < 0".to_string())?,
|
||||||
false => {
|
false => {
|
||||||
let id = system.get_thread_manager().get_obj_addrs().add_semaphore(Semaphore::new(initial_count));
|
let id = system.get_thread_manager().get_obj_addrs().add_semaphore(Semaphore::new(initial_count));
|
||||||
machine.write_int_register(10, id as i64);
|
machine.write_int_register(10, id as i64);
|
||||||
@ -278,12 +278,12 @@ fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result<MachineOk
|
|||||||
};
|
};
|
||||||
let current_thread = current_thread.borrow_mut();
|
let current_thread = current_thread.borrow_mut();
|
||||||
if let Some(process) = current_thread.get_process_owner() {
|
if let Some(process) = current_thread.get_process_owner() {
|
||||||
system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, current_thread.thread_context.int_registers[2] as u64, args);
|
system.get_thread_manager().start_thread(n_thread, Rc::clone(process), func as u64, current_thread.thread_context.int_registers[2] as u64 + machine.page_size, args);
|
||||||
// TODO changé la valeur de sp quand on supportera les addresses virtuels
|
// TODO changé la valeur de sp quand on supportera les addresses virtuels
|
||||||
machine.write_int_register(10, tid as i64);
|
machine.write_int_register(10, tid as i64);
|
||||||
Ok(MachineOk::Ok)
|
Ok(MachineOk::Ok)
|
||||||
} else {
|
} else {
|
||||||
return Err("Process owner of current thread is none")?;
|
Err("Process owner of current thread is none")?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,10 +316,9 @@ fn get_length_param(addr: usize, machine: & Machine) -> usize {
|
|||||||
let mut c = 1;
|
let mut c = 1;
|
||||||
while c != 0 {
|
while c != 0 {
|
||||||
c = machine.read_memory(1, addr + i);
|
c = machine.read_memory(1, addr + i);
|
||||||
i +=1;
|
i += 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
println!("addr: {:x}, i: {}", addr, i + 1);
|
|
||||||
i + 1
|
i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +333,6 @@ fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec<char>
|
|||||||
dest.push(c as char);
|
dest.push(c as char);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
dest.push('\0');
|
|
||||||
|
|
||||||
dest
|
dest
|
||||||
}
|
}
|
||||||
@ -352,8 +350,8 @@ mod test {
|
|||||||
machine.write_int_register(17, SC_SHUTDOWN as i64); // Set type to shutdown
|
machine.write_int_register(17, SC_SHUTDOWN as i64); // Set type to shutdown
|
||||||
// let ecall = Instruction::new(0b000000000000_00000_000_00000_1110011);
|
// let ecall = Instruction::new(0b000000000000_00000_000_00000_1110011);
|
||||||
|
|
||||||
machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011); // ecall
|
machine.write_memory(4, 0, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||||
machine.write_memory(4, 4, 0b000000001010_00000_000_00001_0010011); // r1 <- 10
|
machine.write_memory(4, 4, 0b0000_0000_1010_0000_0000_0000_1001_0011); // r1 <- 10
|
||||||
let mut system = System::new(true);
|
let mut system = System::new(true);
|
||||||
machine.run(&mut system);
|
machine.run(&mut system);
|
||||||
// If the machine was stopped with no error, the shutdown worked
|
// If the machine was stopped with no error, the shutdown worked
|
||||||
@ -379,11 +377,11 @@ mod test {
|
|||||||
machine.write_int_register(11, 5); // String size
|
machine.write_int_register(11, 5); // String size
|
||||||
machine.write_int_register(12, 1); // Console output
|
machine.write_int_register(12, 1); // Console output
|
||||||
|
|
||||||
machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011); // ecall
|
machine.write_memory(4, 0, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||||
machine.write_int_register(17, SC_WRITE as i64); // Set type to write
|
machine.write_int_register(17, SC_WRITE as i64); // Set type to write
|
||||||
|
|
||||||
machine.write_memory(4, 4, 0b000000000000_00000_000_10001_0010011); // r17 <- SC_SHUTDOWN
|
machine.write_memory(4, 4, 0b0000_0000_0000_0000_0000_1000_1001_0011); // r17 <- SC_SHUTDOWN
|
||||||
machine.write_memory(4, 8, 0b000000000000_00000_000_00000_1110011); // ecall
|
machine.write_memory(4, 8, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||||
|
|
||||||
let mut system = System::new(true);
|
let mut system = System::new(true);
|
||||||
machine.run(&mut system);
|
machine.run(&mut system);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
pub num_thread: usize,
|
pub num_thread: usize,
|
||||||
}
|
}
|
@ -74,14 +74,14 @@ impl Lock {
|
|||||||
self.free = false;
|
self.free = false;
|
||||||
self.owner = Option::Some(match thread_manager.get_g_current_thread() {
|
self.owner = Option::Some(match thread_manager.get_g_current_thread() {
|
||||||
Some(th) => {
|
Some(th) => {
|
||||||
Rc::clone(&th)
|
Rc::clone(th)
|
||||||
},
|
},
|
||||||
None => unreachable!()
|
None => unreachable!()
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
match thread_manager.get_g_current_thread() {
|
match thread_manager.get_g_current_thread() {
|
||||||
Some(x) => {
|
Some(x) => {
|
||||||
let x = Rc::clone(&x);
|
let x = Rc::clone(x);
|
||||||
self.waiting_queue.push(Rc::clone(&x));
|
self.waiting_queue.push(Rc::clone(&x));
|
||||||
thread_manager.thread_sleep(machine, Rc::clone(&x));
|
thread_manager.thread_sleep(machine, Rc::clone(&x));
|
||||||
},
|
},
|
||||||
@ -111,7 +111,7 @@ impl Lock {
|
|||||||
Some(thread) => {
|
Some(thread) => {
|
||||||
self.owner = Some(thread);
|
self.owner = Some(thread);
|
||||||
match &self.owner {
|
match &self.owner {
|
||||||
Some(x) => thread_manager.ready_to_run(Rc::clone(&x)),
|
Some(x) => thread_manager.ready_to_run(Rc::clone(x)),
|
||||||
None => ()
|
None => ()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::{rc::Rc, cell::RefCell};
|
use std::{rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use super::process::Process;
|
use super::{process::Process, thread_manager::ThreadRef};
|
||||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}};
|
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::list::List};
|
||||||
|
|
||||||
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ pub struct Thread {
|
|||||||
name: String,
|
name: String,
|
||||||
pub process: Option<Rc<RefCell<Process>>>,
|
pub process: Option<Rc<RefCell<Process>>>,
|
||||||
pub thread_context: ThreadContext,
|
pub thread_context: ThreadContext,
|
||||||
pub stack_pointer: i32,
|
pub join_thread: List<ThreadRef>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
@ -40,9 +40,9 @@ impl Thread {
|
|||||||
thread_context: ThreadContext {
|
thread_context: ThreadContext {
|
||||||
int_registers: [0; NUM_INT_REGS],
|
int_registers: [0; NUM_INT_REGS],
|
||||||
float_registers: [0f32; NUM_FP_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],
|
float_registers: [0f32; NUM_FP_REGS],
|
||||||
pc: 0
|
pc: 0
|
||||||
};
|
};
|
||||||
x.stack_pointer = 0;
|
|
||||||
x }
|
x }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
//! # 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 [`Thread`] instances and synchronization objects, such as
|
||||||
//! `Conditions`.
|
//! [`Lock`](crate::kernel::synch::Lock),
|
||||||
|
//! [`Semaphore`](crate::kernel::synch::Semaphore) and
|
||||||
|
//! [`Condition`](crate::kernel::synch::Condition).
|
||||||
//!
|
//!
|
||||||
//! ## 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
|
||||||
//!
|
//!
|
||||||
@ -24,21 +26,23 @@
|
|||||||
//!
|
//!
|
||||||
//! 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:
|
||||||
//! 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
|
//! - Thread **A** wants to write to a file while **B** is currently reading said file.
|
||||||
//! avoid such issues. Thread **A** will have to wait for **B** to finish reading the 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
|
//! 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`](crate::kernel::system::System) struct.
|
||||||
//! `System` will automatically instanciate a `ThreadManager`
|
//! Instanciating [`System`](crate::kernel::system::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:
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
@ -62,8 +66,8 @@
|
|||||||
//!
|
//!
|
||||||
//! ## 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.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -98,7 +102,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Using this type alias to simplify struct and method definitions
|
/// Using this type alias to simplify struct and method definitions
|
||||||
type ThreadRef = Rc<RefCell<Thread>>;
|
pub type ThreadRef = Rc<RefCell<Thread>>;
|
||||||
|
|
||||||
/// # Thread manager
|
/// # Thread manager
|
||||||
///
|
///
|
||||||
@ -182,6 +186,8 @@ impl ThreadManager {
|
|||||||
|
|
||||||
/// Start a thread, attaching it to a process
|
/// Start a thread, attaching it to a process
|
||||||
pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) {
|
pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) {
|
||||||
|
self.debug(format!("starting thread \"{}\"", thread.borrow().get_name()));
|
||||||
|
|
||||||
let mut thread_m = thread.borrow_mut();
|
let mut thread_m = thread.borrow_mut();
|
||||||
assert_eq!(thread_m.process, Option::None);
|
assert_eq!(thread_m.process, Option::None);
|
||||||
thread_m.process = Option::Some(Rc::clone(&owner));
|
thread_m.process = Option::Some(Rc::clone(&owner));
|
||||||
@ -195,23 +201,29 @@ impl ThreadManager {
|
|||||||
/// Wait for another thread to finish its execution
|
/// Wait for another thread to finish its execution
|
||||||
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
|
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
|
||||||
let waiting_for = Rc::clone(&waiting_for);
|
let waiting_for = Rc::clone(&waiting_for);
|
||||||
while self.get_g_alive().contains(&waiting_for) {
|
if self.get_g_alive().contains(&waiting_for) {
|
||||||
self.debug(format!("Joining \"{}\" to \"{}\"", waiter.borrow().get_name(), waiting_for.borrow().get_name()));
|
waiting_for.borrow_mut().join_thread.push(Rc::clone(&waiter));
|
||||||
self.thread_yield(machine, Rc::clone(&waiter));
|
self.thread_yield(machine, Rc::clone(&waiter), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relinquish the CPU if any other thread is runnable.
|
/// Relinquish the CPU if any other thread is runnable.
|
||||||
///
|
///
|
||||||
/// Cannot use yield as a function name -> reserved name in rust
|
/// 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);
|
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());
|
debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread());
|
||||||
let next_thread = self.find_next_to_run();
|
let next_thread = self.find_next_to_run();
|
||||||
if let Some(next_thread) = next_thread {
|
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);
|
self.switch_to(machine, next_thread);
|
||||||
}
|
}
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
@ -222,6 +234,7 @@ impl ThreadManager {
|
|||||||
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
||||||
debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
|
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();
|
let mut next_thread = self.find_next_to_run();
|
||||||
while next_thread.is_none() {
|
while next_thread.is_none() {
|
||||||
eprintln!("Nobody to run => idle");
|
eprintln!("Nobody to run => idle");
|
||||||
@ -236,8 +249,11 @@ impl ThreadManager {
|
|||||||
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||||
assert!(self.g_alive.remove(Rc::clone(&thread)));
|
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
|
// 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));
|
self.thread_sleep(machine, Rc::clone(&thread));
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
}
|
}
|
||||||
@ -351,29 +367,31 @@ impl ThreadManager {
|
|||||||
|
|
||||||
/// Wake up a waiter if necessary, or release it if no thread is waiting.
|
/// 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<MachineOk, MachineError> {
|
pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
|
||||||
|
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||||
let current_thread = match self.get_g_current_thread() {
|
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."))?
|
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() {
|
let mut lock = match self.get_obj_addrs().search_lock(id) {
|
||||||
Some(lock) => lock,
|
Some(lock) => lock,
|
||||||
None => Err(String::from("lock_release error: cannot find lock."))?
|
None => Err(String::from("lock_release error: cannot find lock."))?
|
||||||
};
|
};
|
||||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
|
||||||
if let Some(lock_owner) = &lock.owner {
|
if let Some(lock_owner) = &lock.owner {
|
||||||
if Rc::ptr_eq(¤t_thread, lock_owner) {
|
if current_thread.eq(lock_owner) { // is_held_by_current_thread
|
||||||
if let Some(thread) = lock.waiting_queue.pop() {
|
match lock.waiting_queue.pop() {
|
||||||
let clone = Rc::clone(&thread);
|
Some(th) => {
|
||||||
lock.owner = Some(thread);
|
lock.owner = Some(Rc::clone(&th));
|
||||||
self.ready_to_run(clone);
|
self.ready_to_run(Rc::clone(&th));
|
||||||
lock.free = true;
|
},
|
||||||
} else {
|
None => {
|
||||||
lock.free = true;
|
lock.free = true;
|
||||||
lock.owner = None;
|
lock.owner = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.get_obj_addrs().update_lock(id, lock);
|
// self.get_obj_addrs().update_lock(id, lock);
|
||||||
|
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
Ok(MachineOk::Ok)
|
Ok(MachineOk::Ok)
|
||||||
}
|
}
|
||||||
@ -428,7 +446,7 @@ mod test {
|
|||||||
|
|
||||||
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(&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_eq!(thread1.borrow_mut().thread_context.pc, start_pc);
|
||||||
debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1)));
|
debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1)));
|
||||||
|
|
||||||
@ -443,7 +461,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lock(){
|
fn test_lock_single(){
|
||||||
let mut machine = Machine::new(true, get_debug_configuration());
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
let mut thread_manager = ThreadManager::new(true);
|
let mut thread_manager = ThreadManager::new(true);
|
||||||
let lock = Lock::new();
|
let lock = Lock::new();
|
||||||
@ -470,6 +488,54 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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")));
|
||||||
|
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()));
|
||||||
|
|
||||||
|
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();
|
||||||
|
assert_eq!(lock.owner,Some(thread_1.clone()));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.set_g_current_thread(Some(thread_2.clone()));
|
||||||
|
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();
|
||||||
|
assert_eq!(lock.owner,Some(thread_1));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert_eq!(lock.waiting_queue.iter().count(),1);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
assert_eq!(lock.owner, Some(thread_2.clone()));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.set_g_current_thread(Some(thread_2));
|
||||||
|
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();
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
assert_eq!(lock.owner, None);
|
||||||
|
assert!(lock.free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_semaphore_single() {
|
fn test_semaphore_single() {
|
||||||
// Init
|
// Init
|
||||||
|
25
src/main.rs
25
src/main.rs
@ -1,4 +1,7 @@
|
|||||||
|
#![doc(
|
||||||
|
html_logo_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg",
|
||||||
|
html_favicon_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg")
|
||||||
|
]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![warn(clippy::missing_docs_in_private_items)]
|
#![warn(clippy::missing_docs_in_private_items)]
|
||||||
|
|
||||||
@ -7,10 +10,8 @@
|
|||||||
//! Burritos is an educational operating system written in Rust
|
//! Burritos is an educational operating system written in Rust
|
||||||
//! running on RISC-V emulator.
|
//! running on RISC-V emulator.
|
||||||
|
|
||||||
/// Contain hardware simulated part of the machine
|
|
||||||
mod simulator;
|
mod simulator;
|
||||||
mod kernel;
|
mod kernel;
|
||||||
/// module containing useful tools which can be use in most part of the OS to ease the development of the OS
|
|
||||||
pub mod utility;
|
pub mod utility;
|
||||||
|
|
||||||
use std::{rc::Rc, cell::RefCell};
|
use std::{rc::Rc, cell::RefCell};
|
||||||
@ -20,7 +21,7 @@ use simulator::{machine::Machine, loader};
|
|||||||
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use utility::cfg::{get_debug_configuration, read_settings};
|
use utility::cfg::read_settings;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System)
|
#[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System)
|
||||||
@ -29,9 +30,13 @@ Burritos is an educational operating system written in Rust
|
|||||||
running on RISC-V emulator.", long_about = None)]
|
running on RISC-V emulator.", long_about = None)]
|
||||||
/// Launch argument parser
|
/// Launch argument parser
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Enable debug mode
|
/// Enable debug mode.
|
||||||
#[arg(short, long)]
|
/// 0 to disable debug,
|
||||||
debug: bool,
|
/// 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), default_value_t = 0)]
|
||||||
|
debug: u8,
|
||||||
/// Path to the executable binary file to execute
|
/// Path to the executable binary file to execute
|
||||||
#[arg(short = 'x', long, value_name = "PATH")]
|
#[arg(short = 'x', long, value_name = "PATH")]
|
||||||
executable: String
|
executable: String
|
||||||
@ -40,10 +45,10 @@ struct Args {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let args = Args::parse();
|
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 (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 = Thread::new(args.executable.as_str());
|
||||||
let thread_exec = Rc::new(RefCell::new(thread_exec));
|
let thread_exec = Rc::new(RefCell::new(thread_exec));
|
||||||
@ -51,7 +56,7 @@ fn main() {
|
|||||||
|
|
||||||
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 + machine.page_size, -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));
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct Interrupt {
|
pub struct Interrupt {
|
||||||
level: InterruptStatus
|
level: InterruptStatus
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ impl Interrupt {
|
|||||||
old
|
old
|
||||||
}
|
}
|
||||||
|
|
||||||
fn one_tick(&self, nb_cycle: i32) {
|
fn one_tick(&self, _nb_cycle: i32) {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ impl Interrupt {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
pub enum InterruptStatus {
|
pub enum InterruptStatus {
|
||||||
InterruptOff,
|
InterruptOff,
|
||||||
InterruptOn
|
InterruptOn
|
||||||
|
@ -619,7 +619,7 @@ mod test {
|
|||||||
assert_eq!(loader.sections[1].virt_addr, 0x4000);
|
assert_eq!(loader.sections[1].virt_addr, 0x4000);
|
||||||
assert_eq!(loader.sections[1].image_offset, 0x1000);
|
assert_eq!(loader.sections[1].image_offset, 0x1000);
|
||||||
assert!(loader.sections[1].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
assert!(loader.sections[1].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||||
assert_eq!(loader.sections[2].virt_addr, 0x400_000);
|
assert_eq!(loader.sections[2].virt_addr, 0x0040_0000);
|
||||||
assert_eq!(loader.sections[2].image_offset, 0x2000);
|
assert_eq!(loader.sections[2].image_offset, 0x2000);
|
||||||
assert!(loader.sections[2].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
assert!(loader.sections[2].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ pub struct Machine {
|
|||||||
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
|
// 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
|
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
|
||||||
num_phy_page: u64,
|
num_phy_page: u64,
|
||||||
page_size: u64,
|
pub page_size: u64,
|
||||||
/// Current machine status
|
/// Current machine status
|
||||||
pub status: MachineStatus
|
pub status: MachineStatus
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ impl Machine {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raise_exception(&mut self, exception: ExceptionType, address : u64, system: &mut System) -> Result<MachineOk, MachineError>{
|
pub fn raise_exception(&mut self, exception: ExceptionType, _address : u64, system: &mut System) -> Result<MachineOk, MachineError>{
|
||||||
self.set_status(MachineStatus::SystemMode);
|
self.set_status(MachineStatus::SystemMode);
|
||||||
// Handle the interruption
|
// Handle the interruption
|
||||||
match exception::call(&exception, self, system) {
|
match exception::call(&exception, self, system) {
|
||||||
|
@ -21,7 +21,7 @@ impl RegisterNum for i64 {}
|
|||||||
impl RegisterNum for f32 {}
|
impl RegisterNum for f32 {}
|
||||||
|
|
||||||
/// Machine register array
|
/// Machine register array
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub struct Register<U: RegisterNum> {
|
pub struct Register<U: RegisterNum> {
|
||||||
/// 32 available registers of type U
|
/// 32 available registers of type U
|
||||||
register: [U; 32]
|
register: [U; 32]
|
||||||
|
@ -22,7 +22,7 @@ impl TranslationTable {
|
|||||||
|
|
||||||
let mut tmp_vec : Vec<PageTableEntry> = Vec::new();
|
let mut tmp_vec : Vec<PageTableEntry> = Vec::new();
|
||||||
|
|
||||||
for i in 0..MaxVirtPages {
|
for _i in 0..MaxVirtPages {
|
||||||
tmp_vec.push(PageTableEntry::create());
|
tmp_vec.push(PageTableEntry::create());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ impl TranslationTable {
|
|||||||
//Assert a mettre dans chacune des fonctions suivantes
|
//Assert a mettre dans chacune des fonctions suivantes
|
||||||
|
|
||||||
pub fn get_max_num_pages(&self) -> u64{
|
pub fn get_max_num_pages(&self) -> u64{
|
||||||
return self.maxNumPages;
|
self.maxNumPages
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_physical_page(&mut self, vpn : u64, physical_page : i32){
|
pub fn set_physical_page(&mut self, vpn : u64, physical_page : i32){
|
||||||
|
@ -93,7 +93,6 @@ pub fn read_settings() -> Result<Settings, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mock configuration for Machine unit testing
|
/// Returns a mock configuration for Machine unit testing
|
||||||
/// FIXME: Does not cover the whole configuration yet
|
|
||||||
pub fn get_debug_configuration() -> Settings {
|
pub fn get_debug_configuration() -> Settings {
|
||||||
let mut settings_map = Settings::new();
|
let mut settings_map = Settings::new();
|
||||||
settings_map.insert(MachineSettingKey::PageSize, 128);
|
settings_map.insert(MachineSettingKey::PageSize, 128);
|
||||||
@ -101,16 +100,23 @@ pub fn get_debug_configuration() -> Settings {
|
|||||||
settings_map
|
settings_map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Filters out empty lines and comments from the reader `BufReader`.
|
||||||
|
///
|
||||||
|
/// Returns a [`Vec<String>`], each entry containing a valid
|
||||||
|
/// line from the input file.
|
||||||
fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> {
|
fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> {
|
||||||
reader.lines()
|
reader.lines()
|
||||||
.map(|l| l.unwrap())
|
.map(|l| l.unwrap())
|
||||||
.filter(|l| !l.is_empty() && !l.starts_with("#"))
|
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a <K, V> pair to a [`Settings`] map.
|
||||||
|
///
|
||||||
|
/// Returns the updated [`Settings`].
|
||||||
fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings {
|
fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings {
|
||||||
let key = MachineSettingKey::from(key);
|
let key = MachineSettingKey::from(key);
|
||||||
let setting = u64::from_str_radix(setting, 10).unwrap_or(0);
|
let setting = str::parse::<u64>(setting).unwrap_or(0);
|
||||||
settings_map.insert(key, setting);
|
settings_map.insert(key, setting);
|
||||||
settings_map
|
settings_map
|
||||||
}
|
}
|
@ -9,11 +9,10 @@ use std::ptr;
|
|||||||
/// These methods wrap unsafe instructions because it doesn't respect borrow rules per example
|
/// 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.)
|
/// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.)
|
||||||
/// or memory leak
|
/// or memory leak
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct List<T: PartialEq> {
|
pub struct List<T: PartialEq> {
|
||||||
head: Link<T>,
|
head: Link<T>,
|
||||||
tail: *mut Node<T>,
|
tail: Link<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Link<T> = *mut Node<T>;
|
type Link<T> = *mut Node<T>;
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! This module contains data type definitions used in other parts the BurritOS
|
||||||
|
//! They are separated from the rest of the operating system so as to promote
|
||||||
|
//! reusability and to separate data constructs proper from state and actions.
|
||||||
pub mod list;
|
pub mod list;
|
||||||
pub mod objaddr;
|
pub mod objaddr;
|
||||||
pub mod cfg;
|
pub mod cfg;
|
@ -17,9 +17,13 @@ use crate::kernel::{synch::{ Semaphore, Lock }, thread::Thread};
|
|||||||
/// calls.
|
/// calls.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct ObjAddr {
|
pub struct ObjAddr {
|
||||||
|
/// Id of the last added object
|
||||||
last_id: i32,
|
last_id: i32,
|
||||||
|
/// List of [Semaphore] added in this struct. Each is keyed with a unique i32 id.
|
||||||
semaphores: HashMap<i32, Semaphore>,
|
semaphores: HashMap<i32, Semaphore>,
|
||||||
|
/// List of [Lock] added in this struct. Each is keyed with a unique i32 id.
|
||||||
locks: HashMap<i32, Lock>,
|
locks: HashMap<i32, Lock>,
|
||||||
|
/// List of threads known by this instance of ObjAddr (useful for managing lock ownership)
|
||||||
threads: HashMap<i32, Rc<RefCell<Thread>>>,
|
threads: HashMap<i32, Rc<RefCell<Thread>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
PROGRAMS = halt.guac prints.guac producteur_consommateur.guac
|
PROGRAMS = halt.guac prints.guac producteur_consommateur.guac join.guac
|
||||||
TOPDIR = ../..
|
TOPDIR = ../..
|
||||||
include $(TOPDIR)/Makefile.rules
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
26
test/syscall_tests/join.c
Normal file
26
test/syscall_tests/join.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "userlib/syscall.h"
|
||||||
|
#include "userlib/libnachos.h"
|
||||||
|
|
||||||
|
void thread1() {
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
n_printf("Hello from th1\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread2() {
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
n_printf("Hello from th2\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
ThreadId th1 = threadCreate("thread 1", thread1);
|
||||||
|
ThreadId th2 = threadCreate("thread 2", thread2);
|
||||||
|
Join(th1);
|
||||||
|
Join(th2);
|
||||||
|
Shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/* Start.s
|
/* Start.s
|
||||||
* Assembly language assist for user programs running on top of Nachos.
|
* Assembly language assist for user programs running on top of BurritOS.
|
||||||
*
|
*
|
||||||
* Since we don't want to pull in the entire C library, we define
|
* Since we don't want to pull in the entire C library, we define
|
||||||
* what we need for a user program here, namely Start and the system
|
* what we need for a user program here, namely Start and the system
|
||||||
|
Reference in New Issue
Block a user