diff --git a/src/kernel/synch.rs b/src/kernel/synch.rs index 77fc56c..dee0917 100644 --- a/src/kernel/synch.rs +++ b/src/kernel/synch.rs @@ -9,6 +9,7 @@ use std::rc::Rc; use super::scheduler::Scheduler; use super::thread_manager::ThreadManager; +/// Structure of a Semaphore used for synchronisation pub struct Semaphore<'t> { counter:i32, @@ -19,6 +20,16 @@ pub struct Semaphore<'t> { impl<'t> Semaphore<'_> { + /// 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 + /// - *current_thread* the current thread + /// - *machine* the machine where the threads are executed pub fn p(&mut self, current_thread: Rc>, machine: &mut Machine){ let old_status = machine.interrupt.set_status(InterruptOff); self.counter -= 1; @@ -29,6 +40,16 @@ impl<'t> Semaphore<'_> { 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, scheduler: &mut Scheduler){ let old_status = machine.interrupt.set_status(InterruptOff); self.counter -= 1; @@ -39,6 +60,9 @@ impl<'t> Semaphore<'_> { } } +/// Lock used for synchronisation, can be interpreted has a Semaphore with a +/// counter of 1 +/// It's used for critical parts pub struct Lock<'t>{ owner: Rc>, @@ -49,7 +73,19 @@ pub struct Lock<'t>{ } impl<'t> Lock<'_> { - pub fn acquire(&mut self, machine: &mut Machine, current_thread: Rc>) { + + /// 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. + /// + /// Note that thread_manager::thread_seep assumes that interrupts are disabled + /// when it is called. + /// + /// ### Parameters + /// - **current_thread** the current thread + /// - **machine** the machine where the threads are executed + pub fn acquire(&mut self, current_thread: Rc>, machine: &mut Machine) { let old_status = machine.interrupt.set_status(InterruptOff); if self.free { @@ -63,10 +99,19 @@ impl<'t> Lock<'_> { machine.interrupt.set_status(old_status); } + /// Wake up a waiter if necessary, or release it if no thread is waiting. + /// We check that the lock is held by the g_current_thread. + /// As with Acquire, this operation must be atomic, so we need to disable + /// interrupts. scheduler::ready_to_run() assumes that threads + /// are disabled when it is called. + /// + /// ### Parameters + /// - **machine** the machine where the code is executed + /// - **scheduler** the scheduler which determine which thread to execute pub fn release(&mut self, machine: &mut Machine, scheduler: &mut Scheduler, current_thread: Rc>) { let old_status = machine.interrupt.set_status(InterruptOff); - if self.is_held_by_current_thread(current_thread) { + if self.held_by_current_thread(current_thread) { if self.waiting_queue.peek() != None { self.owner = self.waiting_queue.pop().unwrap(); scheduler.ready_to_run(Rc::clone(&self.owner)); @@ -78,11 +123,12 @@ impl<'t> Lock<'_> { machine.interrupt.set_status(old_status); } - pub fn is_held_by_current_thread(&mut self, current_thread: Rc>) -> bool { + pub fn held_by_current_thread(&mut self, current_thread: Rc>) -> bool { Rc::ptr_eq(&self.owner, ¤t_thread) } } +/// Structure of a condition used for synchronisation pub struct Condition<'t>{ waiting_queue:List>>, @@ -91,8 +137,14 @@ pub struct Condition<'t>{ } impl<'t> Condition<'_> { - - pub fn wait(&mut self, machine: &mut Machine, current_thread: Rc>) { + + /// Block the calling thread (put it in the wait queue). + /// This operation must be atomic, so we need to disable interrupts. + /// + /// ### Parameters + /// - **current_thread** the current thread + /// - **machine** the machine where threads are executed + pub fn wait(&mut self, current_thread: Rc>, machine: &mut Machine) { let old_status = machine.interrupt.set_status(InterruptOff); self.waiting_queue.push(Rc::clone(¤t_thread)); @@ -101,6 +153,12 @@ impl<'t> Condition<'_> { machine.interrupt.set_status(old_status); } + /// Wake up the first thread of the wait queue (if any). + /// This operation must be atomic, so we need to disable interrupts. + /// + /// ### Parameters + /// - **machine** the machine where the code is executed + /// - **scheduler** the scheduler which determine which thread to execute pub fn signal(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) { let old_status = machine.interrupt.set_status(InterruptOff); @@ -112,6 +170,12 @@ impl<'t> Condition<'_> { } + /// Wake up all threads waiting in the waitqueue of the condition + /// This operation must be atomic, so we need to disable interrupts. + /// + /// ### Parameters + /// - **machine** the machine where the code is executed + /// - **scheduler** the scheduler which determine which thread to execute pub fn broadcast(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) { let old_status = machine.interrupt.set_status(InterruptOff);