From 83df053dc6ae8ff86ebb298b3bf0cde3e5618dd4 Mon Sep 17 00:00:00 2001 From: Quentin Legot Date: Wed, 1 Mar 2023 16:55:17 +0100 Subject: [PATCH] Implement Thread::start and join --- src/kernel/mgerror.rs | 31 ++++ src/kernel/mod.rs | 3 +- src/kernel/scheduler.rs | 14 +- src/kernel/thread.rs | 49 ++++-- src/utility/list.rs | 323 ++++++++++++++++++++++------------------ src/utility/system.rs | 7 +- 6 files changed, 264 insertions(+), 163 deletions(-) create mode 100644 src/kernel/mgerror.rs diff --git a/src/kernel/mgerror.rs b/src/kernel/mgerror.rs new file mode 100644 index 0000000..453a9bd --- /dev/null +++ b/src/kernel/mgerror.rs @@ -0,0 +1,31 @@ + +/// Error enum, use it with Result +pub enum ErrorCode { + INC_ERROR, + OPENFILE_ERROR, + EXEC_FILE_FORMAT_ERROR, + OUT_OF_MEMORY, + + OUT_OF_DISK, + ALREADY_IN_DIRECTORY, + INEXIST_FILE_ERROR, + INEXIST_DIRECTORY_ERROR, + NOSPACE_IN_DIRECTORY, + NOT_A_FILE, + NOT_A_DIRECTORY, + DIRECTORY_NOT_EMPTY, + INVALID_COUNTER, + + /* Invalid typeId fields: */ + INVALID_SEMAPHORE_ID, + INVALID_LOCK_ID, + INVALID_CONDITION_ID, + INVALID_FILE_ID, + INVALID_THREAD_ID, + + /* Other messages */ + WRONG_FILE_ENDIANESS, + NO_ACIA, + + NUMMSGERROR /* Must always be last */ +} \ No newline at end of file diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index a5dc06d..135f0a3 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -1,3 +1,4 @@ mod process; pub mod thread; -mod scheduler; \ No newline at end of file +pub mod scheduler; +pub mod mgerror; \ No newline at end of file diff --git a/src/kernel/scheduler.rs b/src/kernel/scheduler.rs index d2670b3..0e71684 100644 --- a/src/kernel/scheduler.rs +++ b/src/kernel/scheduler.rs @@ -1,10 +1,12 @@ +use std::sync::Arc; + use crate::utility::list::List; use crate::kernel::thread::Thread; use crate::utility::system::{G_CURRENT_THREAD, G_THREAD_TO_BE_DESTROYED}; -struct Scheduler { - ready_list: List +pub struct Scheduler { + ready_list: List> } impl Scheduler { @@ -25,8 +27,8 @@ impl Scheduler { /// ## Pamameter /// /// **thread** is the thread to be put on the read list - pub fn ready_to_run(&mut self, thread: Thread) { - self.ready_list.push_back(thread); + pub fn ready_to_run(&mut self, thread: Arc) { + self.ready_list.push(thread); } /// Return the next thread to be scheduled onto the CPU. @@ -35,8 +37,8 @@ impl Scheduler { /// Thread is removed from the ready list. /// /// **return** Thread thread to be scheduled - pub fn find_next_to_run(&mut self) -> Option { - self.ready_list.pop_back() + pub fn find_next_to_run(&mut self) -> Option> { + self.ready_list.pop() } /// Dispatch the CPU to next_thread. Save the state of the old thread diff --git a/src/kernel/thread.rs b/src/kernel/thread.rs index a4301f8..7f109df 100644 --- a/src/kernel/thread.rs +++ b/src/kernel/thread.rs @@ -1,5 +1,7 @@ -use super::process::Process; -use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::system::ObjectType}; +use std::sync::Arc; + +use super::{process::Process, mgerror::ErrorCode}; +use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::system::{ObjectType, G_ALIVE, G_SCHEDULER}, kernel::scheduler}; const SIMULATORSTACKSIZE: usize = 32 * 1024; @@ -43,15 +45,31 @@ impl Thread { } /// Start a thread, attaching it to a process - pub fn start(&mut self, owner: Process, func: i64, arg: i64) -> i32 { + pub fn start(mut self, owner: Process, func: i64, arg: i64) -> Result<(), ErrorCode> { self.process = Option::Some(owner); let ptr = 0; // todo addrspace self.init_thread_context(func, ptr, arg); let base_stack_addr: [i8; SIMULATORSTACKSIZE] = [0; SIMULATORSTACKSIZE]; // todo AllocBoundedArray self.init_simulator_context(base_stack_addr); self.process.as_mut().unwrap().num_thread += 1; - - todo!(); + match G_ALIVE.write() { + Ok(mut alive) => { + let this = Arc::new(self); + alive.push(Arc::clone(&this)); + match G_SCHEDULER.write() { + Ok(mut scheduler) => { + scheduler.ready_to_run(Arc::clone(&this)); + }, + Err(err) => { + panic!("RwLock poisonned, {}", err); + } + } + }, + Err(err) => { + panic!("RwLock poisonned, {}", err); + } + } + Result::Ok(()) } fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) { @@ -60,11 +78,24 @@ impl Thread { self.thread_context.int_registers[STACK_REG] = initial_sp; } - /// Wait for another thread to finish its execution - pub fn join(&self, id_thread: &Thread) { + fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) { todo!(); } + /// Wait for another thread to finish its execution + pub fn join(&self, id_thread: Arc) { + match G_ALIVE.write() { + Ok(alive) => { + while alive.contains(&Arc::clone(&id_thread)) { + self.t_yield(); + } + }, + Err(err) => { + panic!("RwLock poisonned, {}", err) + } + } + } + /// Relinquish the CPU if any other thread is runnable. /// /// Cannot use yield as a function name -> reserved name in rust @@ -87,10 +118,6 @@ impl Thread { todo!(); } - pub fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) { - todo!(); - } - pub fn save_processor_state(&self) { todo!(); } diff --git a/src/utility/list.rs b/src/utility/list.rs index 46f5557..a90ec2b 100644 --- a/src/utility/list.rs +++ b/src/utility/list.rs @@ -1,178 +1,217 @@ -use std::{cell::RefCell, rc::Rc}; -/// Definition of an element of the list -/// -/// Contain one stored item and the previous/next element of the list -struct ListNode { - item: T, - next: Link, - prev: Link, -} - -impl ListNode { - fn new(item: T) -> Self { - Self { - item, - next: None, - prev: None, - } - } -} - -type Link = Option>>>; - -/// Defintion of the generic linked list -#[derive(Default)] -pub struct DoublyLinkedList { +pub struct List { head: Link, - tail: Link, - size: usize, } -impl DoublyLinkedList { +type Link = Option>>; +struct Node { + elem: T, + next: Link, +} + +impl List { pub fn new() -> Self { - Self { - head: None, - tail: None, - size: 0, - } + List { head: None } } - pub fn is_empty(&self) -> bool { - self.len() == 0 + /// Push an item at the end of the list + pub fn push(&mut self, elem: T) { + let new_node = Box::new(Node { + elem: elem, + next: self.head.take(), + }); + + self.head = Some(new_node); } - pub fn len(&self) -> usize { - self.size - } - - /// Add the item at the end of the list - pub fn push_back(&mut self, item: T) { - let node = Rc::new(RefCell::new(ListNode::new(item))); - if let Some(prev_tail) = self.tail.take() { - prev_tail.borrow_mut().next = Some(Rc::clone(&node)); - node.borrow_mut().prev = Some(prev_tail); - self.tail = Some(node); - self.size += 1; - } else { - self.head = Some(Rc::clone(&node)); - self.tail = Some(node); - self.size = 1; - } - } - - /// Add the item at the start of the list - pub fn push_front(&mut self, item: T) { - let node = Rc::new(RefCell::new(ListNode::new(item))); - if let Some(prev_head) = self.head.take() { - prev_head.borrow_mut().prev = Some(Rc::clone(&node)); - node.borrow_mut().next = Some(prev_head); - self.head = Some(node); - self.size += 1; - } else { - self.head = Some(Rc::clone(&node)); - self.tail = Some(node); - self.size = 1; - } - } - - /// Retrieve and remove the item at the end of the list - pub fn pop_back(&mut self) -> Option { - self.tail.take().map(|prev_tail| { - self.size -= 1; - match prev_tail.borrow_mut().prev.take() { - Some(node) => { - node.borrow_mut().next = None; - self.tail = Some(node); - } - None => { - self.head.take(); - } - } - Rc::try_unwrap(prev_tail).ok().unwrap().into_inner().item + /// Retrieve and remove the item at the end of the list. + /// + /// Return None if list is empty + pub fn pop(&mut self) -> Option { + self.head.take().map(|node| { + self.head = node.next; + node.elem }) } - /// Retrieve and remove the item at the start of the list - pub fn pop_front(&mut self) -> Option { - self.head.take().map(|prev_head| { - self.size -= 1; - match prev_head.borrow_mut().next.take() { - Some(node) => { - node.borrow_mut().prev = None; - self.head = Some(node); - } - None => { - self.tail.take(); - } - } - Rc::try_unwrap(prev_head).ok().unwrap().into_inner().item + /// Retrieve without removing the item at the end of the list + /// + /// Return None if list is empty + pub fn peek(&self) -> Option<&T> { + self.head.as_ref().map(|node| { + &node.elem }) } - + + /// Retrieve without removing the item at the end of the list as mutable + /// + /// Return None if lsit is empty + pub fn peek_mut(&mut self) -> Option<&mut T> { + self.head.as_mut().map(|node| { + &mut node.elem + }) + } + + /// Search for an element in the list + /// + /// Return **bool** true if the list contains the element, false otherwise + /// + /// Worst case complexity of this function is O(n) + pub fn contains(&self, elem: &T) -> bool { + let mut iter = self.iter(); + let element = iter.next(); + while element.is_some() { + if element.unwrap() == elem { + return true; + } + } + false + } + + pub fn into_iter(self) -> IntoIter { + IntoIter(self) + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter { next: self.head.as_deref() } + } + + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { next: self.head.as_deref_mut() } + } } -impl Drop for DoublyLinkedList { - /// list destructor, safely desallocate smart pointer Rc +impl Drop for List { fn drop(&mut self) { - while let Some(node) = self.head.take() { - let _ = node.borrow_mut().prev.take(); - self.head = node.borrow_mut().next.take(); + let mut cur_link = self.head.take(); + while let Some(mut boxed_node) = cur_link { + cur_link = boxed_node.next.take(); } - self.tail.take(); } } -impl IntoIterator for DoublyLinkedList { - type Item = as Iterator>::Item; +pub struct IntoIter(List); - type IntoIter = ListIterator; - - fn into_iter(self) -> Self::IntoIter { - Self::IntoIter::new(self) - } -} - -pub struct ListIterator { - list: DoublyLinkedList, -} - -impl ListIterator { - fn new(list: DoublyLinkedList) -> Self { - Self { list } - } -} - -impl Iterator for ListIterator { +impl Iterator for IntoIter { type Item = T; + fn next(&mut self) -> Option { + // access fields of a tuple struct numerically + self.0.pop() + } +} + +pub struct Iter<'a, T> { + next: Option<&'a Node>, +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.next.map(|node| { + self.next = node.next.as_deref(); + &node.elem + }) + } +} + +pub struct IterMut<'a, T> { + next: Option<&'a mut Node>, +} + +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; fn next(&mut self) -> Option { - self.list.pop_front() + self.next.take().map(|node| { + self.next = node.next.as_deref_mut(); + &mut node.elem + }) } } -impl DoubleEndedIterator for ListIterator { - fn next_back(&mut self) -> Option { - self.list.pop_back() - } -} - -pub type List = DoublyLinkedList; -pub type ListInt = List; - #[cfg(test)] mod test { - - use super::DoublyLinkedList; + use super::List; #[test] - fn test_list_push() { - let mut list = DoublyLinkedList::new(); - list.push_back(5); - list.push_front(45); - assert_eq!(list.pop_front().unwrap(), 45); - assert_eq!(list.pop_front().unwrap(), 5); + fn basics() { + let mut list = List::new(); + + // Check empty list behaves right + assert_eq!(list.pop(), None); + + // Populate list + list.push(1); + list.push(2); + list.push(3); + + // Check normal removal + assert_eq!(list.pop(), Some(3)); + assert_eq!(list.pop(), Some(2)); + + // Push some more just to make sure nothing's corrupted + list.push(4); + list.push(5); + + // Check normal removal + assert_eq!(list.pop(), Some(5)); + assert_eq!(list.pop(), Some(4)); + + // Check exhaustion + assert_eq!(list.pop(), Some(1)); + assert_eq!(list.pop(), None); } + #[test] + fn peek() { + let mut list = List::new(); + assert_eq!(list.peek(), None); + assert_eq!(list.peek_mut(), None); + list.push(1); list.push(2); list.push(3); + + assert_eq!(list.peek(), Some(&3)); + assert_eq!(list.peek_mut(), Some(&mut 3)); + + list.peek_mut().map(|value| { + *value = 42 + }); + + assert_eq!(list.peek(), Some(&42)); + assert_eq!(list.pop(), Some(42)); + } + + #[test] + fn into_iter() { + let mut list = List::new(); + list.push(1); list.push(2); list.push(3); + + let mut iter = list.into_iter(); + assert_eq!(iter.next(), Some(3)); + assert_eq!(iter.next(), Some(2)); + assert_eq!(iter.next(), Some(1)); + assert_eq!(iter.next(), None); + } + + #[test] + fn iter() { + let mut list = List::new(); + list.push(1); list.push(2); list.push(3); + + let mut iter = list.iter(); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&1)); + } + + #[test] + fn iter_mut() { + let mut list = List::new(); + list.push(1); list.push(2); list.push(3); + + let mut iter = list.iter_mut(); + assert_eq!(iter.next(), Some(&mut 3)); + assert_eq!(iter.next(), Some(&mut 2)); + assert_eq!(iter.next(), Some(&mut 1)); + } } \ No newline at end of file diff --git a/src/utility/system.rs b/src/utility/system.rs index 701e0b7..08dc9bf 100644 --- a/src/utility/system.rs +++ b/src/utility/system.rs @@ -1,8 +1,8 @@ -use std::sync::{RwLock, Arc}; +use std::{sync::{RwLock, Arc}}; use lazy_static::lazy_static; -use crate::kernel::thread::Thread; +use crate::kernel::{thread::Thread, scheduler::Scheduler}; use super::list::List; extern crate lazy_static; @@ -10,7 +10,8 @@ extern crate lazy_static; lazy_static! { pub static ref G_CURRENT_THREAD: RwLock> = RwLock::new(Option::None); pub static ref G_THREAD_TO_BE_DESTROYED: RwLock> = RwLock::new(Option::None); - // pub static ref G_ALIVE: Arc>> = Arc::new(RwLock::new(List::new())); + pub static ref G_ALIVE: RwLock>> = RwLock::new(List::new()); + pub static ref G_SCHEDULER: RwLock = RwLock::new(Scheduler::new()); }