diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index 77295248..f2c92af1 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -1,3 +1,5 @@ mod up; +mod mutex; -pub use up::UPSafeCell; \ No newline at end of file +pub use up::UPSafeCell; +pub use mutex::{Mutex, MutexSpin}; diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs new file mode 100644 index 00000000..e486ad71 --- /dev/null +++ b/os/src/sync/mutex.rs @@ -0,0 +1,40 @@ +use super::UPSafeCell; +use crate::task::suspend_current_and_run_next; + +pub trait Mutex: Sync + Send { + fn lock(&self); + fn unlock(&self); +} + +pub struct MutexSpin { + locked: UPSafeCell, +} + +impl MutexSpin { + pub fn new() -> Self { + Self { + locked: unsafe { UPSafeCell::new(false) }, + } + } +} + +impl Mutex for MutexSpin { + fn lock(&self) { + loop { + let mut locked = self.locked.exclusive_access(); + if *locked { + drop(locked); + suspend_current_and_run_next(); + continue; + } else { + *locked = true; + return; + } + } + } + + fn unlock(&self) { + let mut locked = self.locked.exclusive_access(); + *locked = false; + } +} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 84ff73fe..bf436399 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -14,14 +14,19 @@ const SYSCALL_WAITPID: usize = 260; const SYSCALL_THREAD_CREATE: usize = 1000; const SYSCALL_GETTID: usize = 1001; const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; mod fs; mod process; mod thread; +mod sync; use fs::*; use process::*; use thread::*; +use sync::*; pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { @@ -41,6 +46,9 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_THREAD_CREATE => sys_thread_create(args[0]), SYSCALL_GETTID => sys_gettid(), SYSCALL_WAITTID => sys_waittid(args[0]) as isize, + SYSCALL_MUTEX_CREATE => sys_mutex_create(), + SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]), + SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs new file mode 100644 index 00000000..770cd85f --- /dev/null +++ b/os/src/syscall/sync.rs @@ -0,0 +1,40 @@ +use crate::task::current_process; +use crate::sync::MutexSpin; +use alloc::sync::Arc; + +pub fn sys_mutex_create() -> isize { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + if let Some(id) = process_inner + .mutex_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) { + process_inner.mutex_list[id] = Some(Arc::new(MutexSpin::new())); + id as isize + } else { + process_inner.mutex_list.push(Some(Arc::new(MutexSpin::new()))); + process_inner.mutex_list.len() as isize - 1 + } +} + +pub fn sys_mutex_lock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.lock(); + 0 +} + +pub fn sys_mutex_unlock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.unlock(); + 0 +} diff --git a/os/src/task/process.rs b/os/src/task/process.rs index 9c8d53bd..d7c58112 100644 --- a/os/src/task/process.rs +++ b/os/src/task/process.rs @@ -4,7 +4,7 @@ use crate::mm::{ translated_refmut, }; use crate::trap::{TrapContext, trap_handler}; -use crate::sync::UPSafeCell; +use crate::sync::{UPSafeCell, Mutex}; use core::cell::RefMut; use super::id::RecycleAllocator; use super::TaskControlBlock; @@ -32,6 +32,7 @@ pub struct ProcessControlBlockInner { pub fd_table: Vec>>, pub tasks: Vec>>, pub task_res_allocator: RecycleAllocator, + pub mutex_list: Vec>>, } impl ProcessControlBlockInner { @@ -95,6 +96,7 @@ impl ProcessControlBlock { ], tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), })} }); // create a main thread, we should allocate ustack and trap_cx here @@ -207,6 +209,7 @@ impl ProcessControlBlock { fd_table: new_fd_table, tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), })} }); // add child diff --git a/user/src/bin/race_adder_mutex_spin.rs b/user/src/bin/race_adder_mutex_spin.rs new file mode 100644 index 00000000..1612a1bd --- /dev/null +++ b/user/src/bin/race_adder_mutex_spin.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{exit, thread_create, waittid, get_time}; +use user_lib::{mutex_create, mutex_lock, mutex_unlock}; +use alloc::vec::Vec; + +static mut A: usize = 0; +const PER_THREAD: usize = 10000; +const THREAD_COUNT: usize = 8; + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + let a = &mut A as *mut usize; + let cur = a.read_volatile(); + for _ in 0..500 { t = t * t % 10007; } + a.write_volatile(cur + 1); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main() -> i32 { + let start = get_time(); + assert_eq!(mutex_create(), 0); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + 0 +} diff --git a/user/src/lib.rs b/user/src/lib.rs index b6b3e5ac..7b23eb70 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -117,3 +117,8 @@ pub fn waittid(tid: usize) -> isize { } } } + +pub fn mutex_create() -> isize { sys_mutex_create() } +pub fn mutex_lock(mutex_id: usize) { sys_mutex_lock(mutex_id); } +pub fn mutex_unlock(mutex_id: usize) { sys_mutex_unlock(mutex_id); } + diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 63c10781..f7c9c190 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -14,6 +14,9 @@ const SYSCALL_WAITPID: usize = 260; const SYSCALL_THREAD_CREATE: usize = 1000; const SYSCALL_GETTID: usize = 1001; const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -93,3 +96,15 @@ pub fn sys_gettid() -> isize { pub fn sys_waittid(tid: usize) -> isize { syscall(SYSCALL_WAITTID, [tid, 0, 0]) } + +pub fn sys_mutex_create() -> isize { + syscall(SYSCALL_MUTEX_CREATE, [0, 0, 0]) +} + +pub fn sys_mutex_lock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_LOCK, [id, 0, 0]) +} + +pub fn sys_mutex_unlock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_UNLOCK, [id, 0, 0]) +}