Remove old P and V semaphore function and moved tests

This commit is contained in:
Quentin Legot 2023-04-12 15:22:22 +02:00
parent 21f3a72a3d
commit 752b70e448
3 changed files with 115 additions and 113 deletions

View File

@ -27,53 +27,7 @@ impl Semaphore {
pub fn new(counter: i32) -> Semaphore{ pub fn new(counter: i32) -> Semaphore{
Semaphore { counter, waiting_queue: List::default() } Semaphore { counter, waiting_queue: List::default() }
} }
/// 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.
///
/// Note that thread_manager::thread_sleep assumes that interrupts are disabled
/// when it is called.
///
/// ### Parameters TODO Refaire
/// - *current_thread* the current thread
/// - *machine* the machine where the threads are executed
pub fn p(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
let old_status = machine.interrupt.set_status(InterruptOff);
self.counter -= 1;
if self.counter < 0 {
match thread_manager.get_g_current_thread() {
Some(thread) => {
let rc1_thread = Rc::clone(thread);
let rc2_thread = Rc::clone(thread);
self.waiting_queue.push(rc1_thread);
thread_manager.thread_sleep(machine, rc2_thread);
},
None => unreachable!("Current thread should not be None")
}
}
machine.interrupt.set_status(old_status);
}
/// 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
/// - **machine** the machine where the threads are executed
/// - **scheduler** the scheduler which determine which thread to execute
pub fn v(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager){
let old_status = machine.interrupt.set_status(InterruptOff);
self.counter += 1;
match self.waiting_queue.pop() {
Some(thread) => thread_manager.ready_to_run(thread),
None => ()
}
machine.interrupt.set_status(old_status);
}
} }
/// Lock used for synchronisation, can be interpreted has a Semaphore with a /// Lock used for synchronisation, can be interpreted has a Semaphore with a
@ -267,71 +221,7 @@ impl Condition {
mod test { mod test {
use std::{rc::Rc, cell::RefCell}; use std::{rc::Rc, cell::RefCell};
use crate::{kernel::{thread::Thread, synch::{Semaphore, Lock}, thread_manager::ThreadManager}, simulator::machine::Machine}; use crate::{kernel::{thread::Thread, synch::Lock, thread_manager::ThreadManager}, simulator::machine::Machine};
#[test]
fn test_semaphore_single() {
// Init
let mut machine = Machine::new(true);
let mut thread_manager = ThreadManager::new();
let mut semaphore = Semaphore::new(1);
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
semaphore.p(&mut machine, &mut thread_manager);
assert_eq!(semaphore.counter, 0);
assert!(semaphore.waiting_queue.is_empty());
// V
semaphore.v(&mut machine, &mut thread_manager);
assert_eq!(semaphore.counter, 1);
assert!(semaphore.waiting_queue.is_empty());
}
#[test]
fn test_semaphore_multiple() {
// Init
let mut tm = ThreadManager::new();
let mut machine = Machine::new(true);
let mut semaphore = Semaphore::new(2);
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)));
semaphore.p(&mut machine, &mut tm);
assert_eq!(semaphore.counter, 1);
assert!(semaphore.waiting_queue.is_empty());
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
semaphore.p(&mut machine, &mut tm);
assert_eq!(semaphore.counter, 0);
assert!(semaphore.waiting_queue.is_empty());
tm.set_g_current_thread(Some(Rc::clone(&thread3)));
semaphore.p(&mut machine, &mut tm);
assert_eq!(semaphore.counter, -1);
assert!(semaphore.waiting_queue.iter().count() == 1);
// V
semaphore.v(&mut machine, &mut tm);
assert_eq!(semaphore.counter, 0);
assert!(semaphore.waiting_queue.is_empty());
semaphore.v(&mut machine, &mut tm);
assert_eq!(semaphore.counter, 1);
assert!(semaphore.waiting_queue.is_empty());
semaphore.v(&mut machine, &mut tm);
assert_eq!(semaphore.counter, 2);
assert!(semaphore.waiting_queue.is_empty());
}

View File

@ -173,6 +173,16 @@ impl ThreadManager {
machine.pc = t.thread_context.pc; machine.pc = t.thread_context.pc;
} }
/// 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.
///
/// 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
/// - *machine* Current state of the machine
pub fn sem_p(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> { pub fn sem_p(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
match self.get_g_current_thread() { match self.get_g_current_thread() {
Some(thread) => { Some(thread) => {
@ -196,6 +206,16 @@ impl ThreadManager {
} }
} }
/// 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
pub fn sem_v(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> { pub fn sem_v(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
let sema = self.get_obj_addrs().search_semaphore(id_sema as i32); let sema = self.get_obj_addrs().search_semaphore(id_sema as i32);
match sema { match sema {
@ -253,7 +273,7 @@ impl ThreadManager {
mod test { mod test {
use std::{rc::Rc, cell::RefCell}; use std::{rc::Rc, cell::RefCell};
use crate::{simulator::{machine::Machine, loader}, kernel::{system::System, thread::Thread, process::Process}}; use crate::{simulator::{machine::Machine, loader}, kernel::{system::System, thread::Thread, process::Process, thread_manager::{ThreadManager, self}, synch::Semaphore}};
#[test] #[test]
fn test_thread_context() { fn test_thread_context() {
@ -283,4 +303,95 @@ mod test {
machine.run(system); machine.run(system);
} }
#[test]
fn test_semaphore_single() {
// Init
let mut machine = Machine::new(true);
let mut thread_manager = ThreadManager::new();
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
let mut tm = ThreadManager::new();
let mut machine = Machine::new(true);
let mut semaphore = Semaphore::new(2);
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());
}
}
} }

View File

@ -62,6 +62,7 @@ impl ObjAddr {
self.semaphores.remove(&id) self.semaphores.remove(&id)
} }
/// Remove the object of id **id** from self if it exists
pub fn remove_lock(&mut self, id:i32) -> Option<Lock> { pub fn remove_lock(&mut self, id:i32) -> Option<Lock> {
self.locks.remove(&id) self.locks.remove(&id)
} }