diff --git a/.swp b/.swp new file mode 100644 index 000000000..d3b564b4a Binary files /dev/null and b/.swp differ diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs index 021f16fa8..88fd6dc1d 100644 --- a/os/src/lang_items.rs +++ b/os/src/lang_items.rs @@ -11,10 +11,10 @@ fn panic(info: &PanicInfo) -> ! { "[kernel] Panicked at {}:{} {}", location.file(), location.line(), - info.message().unwrap() + info.message() ); } else { - error!("[kernel] Panicked: {}", info.message().unwrap()); + error!("[kernel] Panicked: {}", info.message()); } unsafe { backtrace(); diff --git a/os/src/main.rs b/os/src/main.rs index a5df470e6..35f00a45e 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -1,6 +1,5 @@ #![no_std] #![no_main] -#![feature(panic_info_message)] #![feature(alloc_error_handler)] extern crate alloc; diff --git a/os/src/mm/heap_allocator.rs b/os/src/mm/heap_allocator.rs index 42a6d7697..a7d7d56a9 100644 --- a/os/src/mm/heap_allocator.rs +++ b/os/src/mm/heap_allocator.rs @@ -15,7 +15,7 @@ pub fn init_heap() { unsafe { HEAP_ALLOCATOR .lock() - .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE); + .init(&raw mut HEAP_SPACE as usize, KERNEL_HEAP_SIZE); } } diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 33ff5e4d6..949855f50 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -238,6 +238,9 @@ impl MemorySet { pub fn translate(&self, vpn: VirtPageNum) -> Option { self.page_table.translate(vpn) } + pub fn translate_va(&self, va: VirtAddr) -> Option { + self.page_table.translate_va(va) + } pub fn recycle_data_pages(&mut self) { //*self = Self::new_bare(); self.areas.clear(); diff --git a/os/src/sync/condvar.rs b/os/src/sync/condvar.rs deleted file mode 100644 index baba3cb92..000000000 --- a/os/src/sync/condvar.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::sync::{Mutex, UPSafeCell}; -use crate::task::{block_current_and_run_next, current_task, wakeup_task, TaskControlBlock}; -use alloc::{collections::VecDeque, sync::Arc}; - -pub struct Condvar { - pub inner: UPSafeCell, -} - -pub struct CondvarInner { - pub wait_queue: VecDeque>, -} - -impl Condvar { - pub fn new() -> Self { - Self { - inner: unsafe { - UPSafeCell::new(CondvarInner { - wait_queue: VecDeque::new(), - }) - }, - } - } - - pub fn signal(&self) { - let mut inner = self.inner.exclusive_access(); - if let Some(task) = inner.wait_queue.pop_front() { - wakeup_task(task); - } - } - - pub fn wait(&self, mutex: Arc) { - mutex.unlock(); - let mut inner = self.inner.exclusive_access(); - inner.wait_queue.push_back(current_task().unwrap()); - drop(inner); - block_current_and_run_next(); - mutex.lock(); - } -} diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index 7e948aa6c..4b2bd5a5c 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -1,9 +1,76 @@ -mod condvar; mod mutex; -mod semaphore; mod up; -pub use condvar::Condvar; -pub use mutex::{Mutex, MutexBlocking, MutexSpin}; -pub use semaphore::Semaphore; +pub use mutex::{FUTEX_WAIT, FUTEX_WAKE}; +pub use mutex::{Mutex, FutexQ}; pub use up::UPSafeCell; + +#[inline(always)] +pub fn load_reserved(addr: *const u32) -> u32 { + let val; + unsafe { + core::arch::asm!( + "lr.w {}, ({})", + out(reg) val, + in(reg) addr + ); + } + val +} + +/// return true if successfully modify `addr` in memory +#[inline(always)] +#[allow(unused)] +pub fn store_conditional(addr: *mut u32, val: u32) -> bool { + let res: u32; + unsafe { + core::arch::asm!( + "sc.w {}, {}, ({})", + out(reg) res, + in(reg) val, + in(reg) addr + ); + } + res == 0 +} + +#[inline(always)] +#[allow(unused)] +pub fn atomic_increment(addr: *mut u32) { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val + 1) { break; } + } +} + +#[inline(always)] +#[allow(unused)] +pub fn atomic_decrement(addr: *mut u32) { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val - 1) { break; } + } +} + +#[inline(always)] +#[allow(unused)] +pub fn atomic_test_and_set(addr: *mut u32, bit: u32) -> u32 { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val | (1 << bit)) { return val; } + } +} + +/// 原子地使 `(addr)` 自加 `addend`,并比较结果与 `expected` 是否相等。 +/// 相等则返回 `true` +#[inline(always)] +#[allow(unused)] +pub fn atomic_add_and_compare(addr: *mut u32, addend: u32, expected: u32) -> bool { + loop { + let val = load_reserved(addr); + let res = val + addend; + if store_conditional(addr, res) { + return res == expected + } + } +} \ No newline at end of file diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs index 5afcba041..cb512229f 100644 --- a/os/src/sync/mutex.rs +++ b/os/src/sync/mutex.rs @@ -1,88 +1,66 @@ -use super::UPSafeCell; +use super::{load_reserved, store_conditional}; +use core::cell::UnsafeCell; use crate::task::TaskControlBlock; -use crate::task::{block_current_and_run_next, suspend_current_and_run_next}; -use crate::task::{current_task, wakeup_task}; use alloc::{collections::VecDeque, sync::Arc}; +pub const FUTEX_WAIT: usize = 0; +pub const FUTEX_WAKE: usize = 1; pub trait Mutex: Sync + Send { fn lock(&self); fn unlock(&self); } pub struct MutexSpin { - locked: UPSafeCell, + locked: UnsafeCell, } +unsafe impl Send for MutexSpin {} +unsafe impl Sync for MutexSpin {} + impl MutexSpin { pub fn new() -> Self { Self { - locked: unsafe { UPSafeCell::new(false) }, + locked: UnsafeCell::new(0), } } } impl Mutex for MutexSpin { fn lock(&self) { + let addr = self.locked.get(); loop { - let mut locked = self.locked.exclusive_access(); - if *locked { - drop(locked); - suspend_current_and_run_next(); - continue; - } else { - *locked = true; - return; - } + while load_reserved(addr) == 1 {} + if store_conditional(addr, 1) { return; } } } fn unlock(&self) { - let mut locked = self.locked.exclusive_access(); - *locked = false; + let addr = self.locked.get(); + unsafe { *addr = 0; } } } -pub struct MutexBlocking { - inner: UPSafeCell, -} - -pub struct MutexBlockingInner { - locked: bool, - wait_queue: VecDeque>, +// Futex的等待队列,由位于内核的进程控制块维护 +pub struct FutexQ { + pub guard: MutexSpin, + queue: UnsafeCell>>, } -impl MutexBlocking { +impl FutexQ { pub fn new() -> Self { Self { - inner: unsafe { - UPSafeCell::new(MutexBlockingInner { - locked: false, - wait_queue: VecDeque::new(), - }) - }, + guard: MutexSpin::new(), + queue: UnsafeCell::new(VecDeque::new()), } } -} -impl Mutex for MutexBlocking { - fn lock(&self) { - let mut mutex_inner = self.inner.exclusive_access(); - if mutex_inner.locked { - mutex_inner.wait_queue.push_back(current_task().unwrap()); - drop(mutex_inner); - block_current_and_run_next(); - } else { - mutex_inner.locked = true; - } - } + /// 外部调用必须确保已拿到 FutexQ 的自旋锁 + pub fn push_back(&self, task: Arc) { + unsafe { &mut *self.queue.get() }.push_back(task); + } - fn unlock(&self) { - let mut mutex_inner = self.inner.exclusive_access(); - assert!(mutex_inner.locked); - if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { - wakeup_task(waking_task); - } else { - mutex_inner.locked = false; - } + /// 外部调用必须确保已拿到 FutexQ 的自旋锁 + pub fn pop_front(&self) -> Option> { + unsafe { &mut *self.queue.get() }.pop_front() } -} +} \ No newline at end of file diff --git a/os/src/sync/semaphore.rs b/os/src/sync/semaphore.rs deleted file mode 100644 index dfbd12bf7..000000000 --- a/os/src/sync/semaphore.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::sync::UPSafeCell; -use crate::task::{block_current_and_run_next, current_task, wakeup_task, TaskControlBlock}; -use alloc::{collections::VecDeque, sync::Arc}; - -pub struct Semaphore { - pub inner: UPSafeCell, -} - -pub struct SemaphoreInner { - pub count: isize, - pub wait_queue: VecDeque>, -} - -impl Semaphore { - pub fn new(res_count: usize) -> Self { - Self { - inner: unsafe { - UPSafeCell::new(SemaphoreInner { - count: res_count as isize, - wait_queue: VecDeque::new(), - }) - }, - } - } - - pub fn up(&self) { - let mut inner = self.inner.exclusive_access(); - inner.count += 1; - if inner.count <= 0 { - if let Some(task) = inner.wait_queue.pop_front() { - wakeup_task(task); - } - } - } - - pub fn down(&self) { - let mut inner = self.inner.exclusive_access(); - inner.count -= 1; - if inner.count < 0 { - inner.wait_queue.push_back(current_task().unwrap()); - drop(inner); - block_current_and_run_next(); - } - } -} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index ca3fc526c..3da11327b 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,30 +1,22 @@ -const SYSCALL_DUP: usize = 24; -const SYSCALL_OPEN: usize = 56; -const SYSCALL_CLOSE: usize = 57; -const SYSCALL_PIPE: usize = 59; -const SYSCALL_READ: usize = 63; -const SYSCALL_WRITE: usize = 64; -const SYSCALL_EXIT: usize = 93; -const SYSCALL_SLEEP: usize = 101; -const SYSCALL_YIELD: usize = 124; -const SYSCALL_KILL: usize = 129; -const SYSCALL_GET_TIME: usize = 169; -const SYSCALL_GETPID: usize = 172; -const SYSCALL_FORK: usize = 220; -const SYSCALL_EXEC: usize = 221; -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; -const SYSCALL_SEMAPHORE_CREATE: usize = 1020; -const SYSCALL_SEMAPHORE_UP: usize = 1021; -const SYSCALL_SEMAPHORE_DOWN: usize = 1022; -const SYSCALL_CONDVAR_CREATE: usize = 1030; -const SYSCALL_CONDVAR_SIGNAL: usize = 1031; -const SYSCALL_CONDVAR_WAIT: usize = 1032; +pub const SYSCALL_DUP: usize = 24; +pub const SYSCALL_OPEN: usize = 56; +pub const SYSCALL_CLOSE: usize = 57; +pub const SYSCALL_PIPE: usize = 59; +pub const SYSCALL_READ: usize = 63; +pub const SYSCALL_WRITE: usize = 64; +pub const SYSCALL_EXIT: usize = 93; +pub const SYSCALL_SLEEP: usize = 101; +pub const SYSCALL_YIELD: usize = 124; +pub const SYSCALL_KILL: usize = 129; +pub const SYSCALL_GET_TIME: usize = 169; +pub const SYSCALL_GETPID: usize = 172; +pub const SYSCALL_FORK: usize = 220; +pub const SYSCALL_EXEC: usize = 221; +pub const SYSCALL_FUTEX: usize = 240; +pub const SYSCALL_WAITPID: usize = 260; +pub const SYSCALL_THREAD_CREATE: usize = 1000; +pub const SYSCALL_GETTID: usize = 1001; +pub const SYSCALL_WAITTID: usize = 1002; mod fs; mod process; @@ -36,7 +28,7 @@ use process::*; use sync::*; use thread::*; -pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { +pub fn syscall_handler(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { SYSCALL_DUP => sys_dup(args[0]), SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), @@ -52,19 +44,11 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_GETPID => sys_getpid(), SYSCALL_FORK => sys_fork(), SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), + SYSCALL_FUTEX => sys_futex(args[0] as *const i32, args[1], args[2]), SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]), SYSCALL_GETTID => sys_gettid(), SYSCALL_WAITTID => sys_waittid(args[0]) as isize, - SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1), - SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]), - SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]), - SYSCALL_SEMAPHORE_CREATE => sys_semaphore_create(args[0]), - SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]), - SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]), - SYSCALL_CONDVAR_CREATE => sys_condvar_create(), - SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]), - SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs index eb67583ad..2433595bf 100644 --- a/os/src/syscall/sync.rs +++ b/os/src/syscall/sync.rs @@ -1,7 +1,8 @@ -use crate::sync::{Condvar, Mutex, MutexBlocking, MutexSpin, Semaphore}; -use crate::task::{block_current_and_run_next, current_process, current_task}; +use crate::sync::{Mutex, FutexQ}; +use crate::task::{TaskStatus, TaskContext}; +use crate::task::{block_current_and_run_next, current_process, current_task, schedule, take_current_task, wakeup_task}; use crate::timer::{add_timer, get_time_ms}; -use alloc::sync::Arc; +use crate::sync::{FUTEX_WAIT, FUTEX_WAKE}; pub fn sys_sleep(ms: usize) -> isize { let expire_ms = get_time_ms() + ms; @@ -11,124 +12,47 @@ pub fn sys_sleep(ms: usize) -> isize { 0 } -pub fn sys_mutex_create(blocking: bool) -> isize { - let process = current_process(); - let mutex: Option> = if !blocking { - Some(Arc::new(MutexSpin::new())) - } else { - Some(Arc::new(MutexBlocking::new())) - }; - 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] = mutex; - id as isize - } else { - process_inner.mutex_list.push(mutex); - 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 -} - -pub fn sys_semaphore_create(res_count: usize) -> isize { - let process = current_process(); - let mut process_inner = process.inner_exclusive_access(); - let id = if let Some(id) = process_inner - .semaphore_list - .iter() - .enumerate() - .find(|(_, item)| item.is_none()) - .map(|(id, _)| id) - { - process_inner.semaphore_list[id] = Some(Arc::new(Semaphore::new(res_count))); - id - } else { - process_inner - .semaphore_list - .push(Some(Arc::new(Semaphore::new(res_count)))); - process_inner.semaphore_list.len() - 1 - }; - id as isize -} - -pub fn sys_semaphore_up(sem_id: usize) -> isize { - let process = current_process(); - let process_inner = process.inner_exclusive_access(); - let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); - drop(process_inner); - sem.up(); - 0 -} - -pub fn sys_semaphore_down(sem_id: usize) -> isize { - let process = current_process(); - let process_inner = process.inner_exclusive_access(); - let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); - drop(process_inner); - sem.down(); - 0 -} - -pub fn sys_condvar_create() -> isize { +/// 封装在 Futex 方法中,不应直接调用 +pub fn sys_futex(uaddr: *const i32, futex_op: usize, val: usize) -> isize{ let process = current_process(); let mut process_inner = process.inner_exclusive_access(); - let id = if let Some(id) = process_inner - .condvar_list - .iter() - .enumerate() - .find(|(_, item)| item.is_none()) - .map(|(id, _)| id) - { - process_inner.condvar_list[id] = Some(Arc::new(Condvar::new())); - id - } else { - process_inner - .condvar_list - .push(Some(Arc::new(Condvar::new()))); - process_inner.condvar_list.len() - 1 + let phys_addr = process_inner + .memory_set + .translate_va((uaddr as usize).into()) + .unwrap() + .0 as *const i32; + // 如果 Futex 第一次调用 sys_futex,则插入新的队列 + let futex_q = process_inner.futex_queues + .entry(uaddr as usize) + .or_insert(FutexQ::new()); + match futex_op { + FUTEX_WAIT => { + futex_q.guard.lock(); + if unsafe { *phys_addr == (val as i32)} { + // *addr等于预期值,标记 task 为阻塞,加入 futex 等待队列 + let task = take_current_task().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Blocked; + let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext; + drop(task_inner); + futex_q.push_back(task); + futex_q.guard.unlock(); + // switch to other task + drop(process_inner); + schedule(task_cx_ptr); + } else { + futex_q.guard.unlock(); + return 0; + } + }, + FUTEX_WAKE => { + futex_q.guard.lock(); + if let Some(task) = futex_q.pop_front() { + wakeup_task(task); + } + futex_q.guard.unlock(); + }, + _ => panic!("Unsupported futex_op: {}", futex_op) }; - id as isize -} - -pub fn sys_condvar_signal(condvar_id: usize) -> isize { - let process = current_process(); - let process_inner = process.inner_exclusive_access(); - let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); - drop(process_inner); - condvar.signal(); - 0 -} - -pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { - let process = current_process(); - let process_inner = process.inner_exclusive_access(); - let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); - let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); - drop(process_inner); - condvar.wait(mutex); 0 -} +} \ No newline at end of file diff --git a/os/src/task/process.rs b/os/src/task/process.rs index ffff14fe7..c0783fb5c 100644 --- a/os/src/task/process.rs +++ b/os/src/task/process.rs @@ -5,12 +5,13 @@ use super::{add_task, SignalFlags}; use super::{pid_alloc, PidHandle}; use crate::fs::{File, Stdin, Stdout}; use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE}; -use crate::sync::{Condvar, Mutex, Semaphore, UPSafeCell}; +use crate::sync::{UPSafeCell, FutexQ}; use crate::trap::{trap_handler, TrapContext}; use alloc::string::String; use alloc::sync::{Arc, Weak}; use alloc::vec; use alloc::vec::Vec; +use alloc::collections::btree_map::BTreeMap; use core::cell::RefMut; pub struct ProcessControlBlock { @@ -30,9 +31,7 @@ pub struct ProcessControlBlockInner { pub signals: SignalFlags, pub tasks: Vec>>, pub task_res_allocator: RecycleAllocator, - pub mutex_list: Vec>>, - pub semaphore_list: Vec>>, - pub condvar_list: Vec>>, + pub futex_queues: BTreeMap, } impl ProcessControlBlockInner { @@ -97,9 +96,7 @@ impl ProcessControlBlock { signals: SignalFlags::empty(), tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), - mutex_list: Vec::new(), - semaphore_list: Vec::new(), - condvar_list: Vec::new(), + futex_queues: BTreeMap::new(), }) }, }); @@ -216,9 +213,7 @@ impl ProcessControlBlock { signals: SignalFlags::empty(), tasks: Vec::new(), task_res_allocator: RecycleAllocator::new(), - mutex_list: Vec::new(), - semaphore_list: Vec::new(), - condvar_list: Vec::new(), + futex_queues: BTreeMap::new(), }) }, }); diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 6397811ae..c0bcd4455 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -1,7 +1,7 @@ mod context; use crate::config::TRAMPOLINE; -use crate::syscall::syscall; +use crate::syscall::syscall_handler; use crate::task::{ check_signals_of_current, current_add_signal, current_trap_cx, current_trap_cx_user_va, current_user_token, exit_current_and_run_next, suspend_current_and_run_next, SignalFlags, @@ -49,7 +49,7 @@ pub fn trap_handler() -> ! { let mut cx = current_trap_cx(); cx.sepc += 4; // get system call return value - let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]); + let result = syscall_handler(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]); // cx is changed during sys_exec, so we have to call it again cx = current_trap_cx(); cx.x[10] = result as usize; diff --git a/user/src/bin/adder_mutex_blocking.rs b/user/src/bin/adder_mutex_blocking.rs index 7fcb80bd8..609f5848e 100644 --- a/user/src/bin/adder_mutex_blocking.rs +++ b/user/src/bin/adder_mutex_blocking.rs @@ -8,12 +8,16 @@ extern crate alloc; use alloc::vec::Vec; use core::ptr::addr_of_mut; use user_lib::{exit, get_time, thread_create, waittid}; -use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; +use user_lib::Futex; +use lazy_static::lazy_static; static mut A: usize = 0; const PER_THREAD_DEFAULT: usize = 10000; const THREAD_COUNT_DEFAULT: usize = 16; static mut PER_THREAD: usize = 0; +lazy_static! { + static ref mutex: Futex = Futex::new(); +} unsafe fn critical_section(t: &mut usize) { let a = addr_of_mut!(A); @@ -23,12 +27,15 @@ unsafe fn critical_section(t: &mut usize) { } a.write_volatile(cur + 1); } -unsafe fn f() -> ! { +unsafe fn f(_id: usize) -> ! { let mut t = 2usize; for _ in 0..PER_THREAD { - mutex_lock(0); + // println!("Thread {} is getting lock...", id); + mutex.lock(); + // println!("Thread {} already get lock", id); critical_section(&mut t); - mutex_unlock(0); + mutex.unlock(); + // println!("Thread {} release lock", id); } exit(t as i32) } @@ -48,10 +55,9 @@ pub fn main(argc: usize, argv: &[&str]) -> i32 { } let start = get_time(); - assert_eq!(mutex_blocking_create(), 0); let mut v = Vec::new(); - for _ in 0..thread_count { - v.push(thread_create(f as usize, 0) as usize); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); } for tid in v.into_iter() { waittid(tid); diff --git a/user/src/bin/adder_mutex_spin.rs b/user/src/bin/adder_mutex_spin.rs index f5750af86..9f7fb50bf 100644 --- a/user/src/bin/adder_mutex_spin.rs +++ b/user/src/bin/adder_mutex_spin.rs @@ -8,12 +8,16 @@ extern crate alloc; use alloc::vec::Vec; use core::ptr::addr_of_mut; use user_lib::{exit, get_time, thread_create, waittid}; -use user_lib::{mutex_create, mutex_lock, mutex_unlock}; +use user_lib::MutexSpin; +use lazy_static::lazy_static; static mut A: usize = 0; const PER_THREAD_DEFAULT: usize = 10000; const THREAD_COUNT_DEFAULT: usize = 16; static mut PER_THREAD: usize = 0; +lazy_static! { + static ref spinlock: MutexSpin = MutexSpin::new(); +} unsafe fn critical_section(t: &mut usize) { let a = addr_of_mut!(A); @@ -27,9 +31,9 @@ unsafe fn critical_section(t: &mut usize) { unsafe fn f() -> ! { let mut t = 2usize; for _ in 0..PER_THREAD { - mutex_lock(0); + spinlock.lock(); critical_section(&mut t); - mutex_unlock(0); + spinlock.unlock(); } exit(t as i32) } @@ -49,7 +53,6 @@ pub fn main(argc: usize, argv: &[&str]) -> 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, 0) as usize); diff --git a/user/src/bin/adder_sem.rs b/user/src/bin/adder_sem.rs new file mode 100644 index 000000000..9631e61c1 --- /dev/null +++ b/user/src/bin/adder_sem.rs @@ -0,0 +1,68 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::Semaphore; +use lazy_static::lazy_static; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; +lazy_static! { + static ref mutex: Semaphore = Semaphore::new(1); +} + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} +unsafe fn f(_id: usize) -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + // println!("Thread {} is getting lock...", id); + mutex.wait(); + // println!("Thread {} already get lock", id); + critical_section(&mut t); + mutex.post(); + // println!("Thread {} release lock", id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + let start = get_time(); + let mut v = Vec::new(); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/barrier_condvar.rs b/user/src/bin/barrier_condvar.rs index 715777231..d97fca7ba 100644 --- a/user/src/bin/barrier_condvar.rs +++ b/user/src/bin/barrier_condvar.rs @@ -9,28 +9,28 @@ use alloc::vec::Vec; use core::cell::UnsafeCell; use lazy_static::*; use user_lib::{ - condvar_create, condvar_signal, condvar_wait, exit, mutex_create, mutex_lock, mutex_unlock, + exit, MutexSpin, Condvar, thread_create, waittid, }; const THREAD_NUM: usize = 3; struct Barrier { - mutex_id: usize, - condvar_id: usize, + mutex: MutexSpin, + condvar: Condvar, count: UnsafeCell, } impl Barrier { pub fn new() -> Self { Self { - mutex_id: mutex_create() as usize, - condvar_id: condvar_create() as usize, + mutex: MutexSpin::new(), + condvar: Condvar::new(), count: UnsafeCell::new(0), } } pub fn block(&self) { - mutex_lock(self.mutex_id); + self.mutex.lock(); let count = self.count.get(); // SAFETY: Here, the accesses of the count is in the // critical section protected by the mutex. @@ -38,12 +38,12 @@ impl Barrier { *count = *count + 1; } if unsafe { *count } == THREAD_NUM { - condvar_signal(self.condvar_id); + self.condvar.notify_one(); } else { - condvar_wait(self.condvar_id, self.mutex_id); - condvar_signal(self.condvar_id); + self.condvar.wait(&self.mutex); + self.condvar.notify_one(); } - mutex_unlock(self.mutex_id); + self.mutex.unlock(); } } diff --git a/user/src/bin/condsync_condvar.rs b/user/src/bin/condsync_condvar.rs index b83aeda21..0b2b07981 100644 --- a/user/src/bin/condsync_condvar.rs +++ b/user/src/bin/condsync_condvar.rs @@ -9,42 +9,42 @@ extern crate alloc; use alloc::vec; use user_lib::exit; use user_lib::{ - condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, + MutexSpin, Condvar }; use user_lib::{sleep, thread_create, waittid}; +use lazy_static::lazy_static; static mut A: usize = 0; -const CONDVAR_ID: usize = 0; -const MUTEX_ID: usize = 0; +lazy_static! { + static ref mutex: MutexSpin = MutexSpin::new(); + static ref condvar: Condvar = Condvar::new(); +} unsafe fn first() -> ! { sleep(10); println!("First work, Change A --> 1 and wakeup Second"); - mutex_lock(MUTEX_ID); + mutex.lock(); A = 1; - condvar_signal(CONDVAR_ID); - mutex_unlock(MUTEX_ID); + condvar.notify_one(); + mutex.unlock(); exit(0) } unsafe fn second() -> ! { println!("Second want to continue,but need to wait A=1"); - mutex_lock(MUTEX_ID); + mutex.lock(); while A == 0 { - println!("Second: A is {}", A); - condvar_wait(CONDVAR_ID, MUTEX_ID); + println!("Second: A is {}", A as usize); + condvar.wait(&mutex); } - println!("A is {}, Second can work now", A); - mutex_unlock(MUTEX_ID); + println!("A is {}, Second can work now", A as usize); + mutex.unlock(); exit(0) } #[no_mangle] pub fn main() -> i32 { - // create condvar & mutex - assert_eq!(condvar_create() as usize, CONDVAR_ID); - assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); // create threads let threads = vec![ thread_create(first as usize, 0), diff --git a/user/src/bin/condsync_sem.rs b/user/src/bin/condsync_sem.rs index ee08face4..ce23b92a6 100644 --- a/user/src/bin/condsync_sem.rs +++ b/user/src/bin/condsync_sem.rs @@ -9,47 +9,47 @@ extern crate alloc; use alloc::vec; use user_lib::exit; use user_lib::{ - mutex_blocking_create, mutex_lock, mutex_unlock, semaphore_create, semaphore_down, semaphore_up, + MutexSpin, Semaphore, }; use user_lib::{sleep, thread_create, waittid}; +use lazy_static::lazy_static; static mut A: usize = 0; - -const SEM_ID: usize = 0; -const MUTEX_ID: usize = 0; +lazy_static! { + static ref mutex: MutexSpin = MutexSpin::new(); + static ref sem: Semaphore = Semaphore::new(0); +} unsafe fn first() -> ! { sleep(10); println!("First work, Change A --> 1 and wakeup Second"); - mutex_lock(MUTEX_ID); + mutex.lock(); A = 1; - semaphore_up(SEM_ID); - mutex_unlock(MUTEX_ID); + println!("First is going to sem.post()"); + sem.post(); + mutex.unlock(); exit(0) } unsafe fn second() -> ! { println!("Second want to continue,but need to wait A=1"); loop { - mutex_lock(MUTEX_ID); + mutex.lock(); if A == 0 { - println!("Second: A is {}", A); - mutex_unlock(MUTEX_ID); - semaphore_down(SEM_ID); + println!("Second: A is {}", A as usize); + mutex.unlock(); + sem.wait(); } else { - mutex_unlock(MUTEX_ID); + mutex.unlock(); break; } } - println!("A is {}, Second can work now", A); + println!("A is {}, Second can work now", A as usize); exit(0) } #[no_mangle] pub fn main() -> i32 { - // create semaphore & mutex - assert_eq!(semaphore_create(0) as usize, SEM_ID); - assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); // create threads let threads = vec![ thread_create(first as usize, 0), diff --git a/user/src/bin/eisenberg.rs b/user/src/bin/eisenberg.rs index 49a1d4549..6c9bdcb4d 100644 --- a/user/src/bin/eisenberg.rs +++ b/user/src/bin/eisenberg.rs @@ -7,9 +7,7 @@ extern crate alloc; extern crate core; use alloc::vec::Vec; -use core::{ - sync::atomic::{AtomicUsize, Ordering}, -}; +use core::sync::atomic::{AtomicUsize, Ordering}; use user_lib::{exit, sleep, thread_create, waittid}; const N: usize = 2; diff --git a/user/src/bin/mpsc_sem.rs b/user/src/bin/mpsc_sem.rs index 7b72bbb17..8b839bda9 100644 --- a/user/src/bin/mpsc_sem.rs +++ b/user/src/bin/mpsc_sem.rs @@ -9,12 +9,10 @@ extern crate alloc; use alloc::vec::Vec; use user_lib::exit; -use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::Semaphore; use user_lib::{thread_create, waittid}; +use lazy_static::lazy_static; -const SEM_MUTEX: usize = 0; -const SEM_EMPTY: usize = 1; -const SEM_AVAIL: usize = 2; const BUFFER_SIZE: usize = 8; static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; static mut FRONT: usize = 0; @@ -22,27 +20,33 @@ static mut TAIL: usize = 0; const PRODUCER_COUNT: usize = 4; const NUMBER_PER_PRODUCER: usize = 100; +lazy_static! { + static ref mutex: Semaphore = Semaphore::new(1); + static ref empty: Semaphore = Semaphore::new(BUFFER_SIZE as i32); + static ref avail: Semaphore = Semaphore::new(0); +} + unsafe fn producer(id: *const usize) -> ! { let id = *id; for _ in 0..NUMBER_PER_PRODUCER { - semaphore_down(SEM_EMPTY); - semaphore_down(SEM_MUTEX); + empty.wait(); + mutex.wait(); BUFFER[TAIL] = id; TAIL = (TAIL + 1) % BUFFER_SIZE; - semaphore_up(SEM_MUTEX); - semaphore_up(SEM_AVAIL); + mutex.post(); + avail.post(); } exit(0) } unsafe fn consumer() -> ! { for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { - semaphore_down(SEM_AVAIL); - semaphore_down(SEM_MUTEX); + avail.wait(); + mutex.wait(); print!("{} ", BUFFER[FRONT]); FRONT = (FRONT + 1) % BUFFER_SIZE; - semaphore_up(SEM_MUTEX); - semaphore_up(SEM_EMPTY); + mutex.post(); + empty.post(); } println!(""); exit(0) @@ -50,10 +54,6 @@ unsafe fn consumer() -> ! { #[no_mangle] pub fn main() -> i32 { - // create semaphores - assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); - assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); - assert_eq!(semaphore_create(0) as usize, SEM_AVAIL); // create threads let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); let mut threads = Vec::new(); diff --git a/user/src/bin/phil_din_mutex.rs b/user/src/bin/phil_din_mutex.rs index 8e7b56677..f4e18e3ad 100644 --- a/user/src/bin/phil_din_mutex.rs +++ b/user/src/bin/phil_din_mutex.rs @@ -8,13 +8,17 @@ extern crate alloc; use alloc::vec::Vec; use user_lib::{exit, get_time, sleep}; -use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; +use user_lib::MutexSpin; use user_lib::{thread_create, waittid}; +use lazy_static::lazy_static; const N: usize = 5; const ROUND: usize = 4; // A round: think -> wait for forks -> eat const GRAPH_SCALE: usize = 100; +lazy_static! { + static ref mutex: [MutexSpin; N] = [MutexSpin::new(), MutexSpin::new(), MutexSpin::new(), MutexSpin::new(), MutexSpin::new()]; +} fn get_time_u() -> usize { get_time() as usize @@ -47,8 +51,8 @@ fn philosopher_dining_problem(id: *const usize) { THINK[id][2 * round + 1] = get_time_u(); } // wait for forks - mutex_lock(min); - mutex_lock(max); + mutex[min].lock(); + mutex[max].lock(); // eating unsafe { EAT[id][2 * round] = get_time_u(); @@ -57,8 +61,8 @@ fn philosopher_dining_problem(id: *const usize) { unsafe { EAT[id][2 * round + 1] = get_time_u(); } - mutex_unlock(max); - mutex_unlock(min); + mutex[max].unlock(); + mutex[min].unlock(); } exit(0) } @@ -69,7 +73,6 @@ pub fn main() -> i32 { let ids: Vec<_> = (0..N).collect(); let start = get_time_u(); for i in 0..N { - assert_eq!(mutex_blocking_create(), i as isize); v.push(thread_create( philosopher_dining_problem as usize, &ids.as_slice()[i] as *const _ as usize, diff --git a/user/src/bin/stackful_coroutine.rs b/user/src/bin/stackful_coroutine.rs index 57ac03226..2bbafc38c 100644 --- a/user/src/bin/stackful_coroutine.rs +++ b/user/src/bin/stackful_coroutine.rs @@ -10,7 +10,7 @@ extern crate alloc; #[macro_use] extern crate user_lib; -use core::arch::asm; +use core::arch::naked_asm; //#[macro_use] use alloc::vec; @@ -262,7 +262,7 @@ pub fn yield_task() { #[no_mangle] unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) { // a0: _old, a1: _new - asm!( + naked_asm!( " sd x1, 0x00(a0) sd x2, 0x08(a0) @@ -298,7 +298,6 @@ unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) { jr t0 ", - options(noreturn) ); } diff --git a/user/src/bin/sync_sem.rs b/user/src/bin/sync_sem.rs index b8d1f79c2..15af35dee 100644 --- a/user/src/bin/sync_sem.rs +++ b/user/src/bin/sync_sem.rs @@ -8,29 +8,29 @@ extern crate alloc; use alloc::vec; use user_lib::exit; -use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::Semaphore; use user_lib::{sleep, thread_create, waittid}; - -const SEM_SYNC: usize = 0; +use lazy_static::lazy_static; +lazy_static! { + static ref sem: Semaphore = Semaphore::new(0); +} unsafe fn first() -> ! { sleep(10); println!("First work and wakeup Second"); - semaphore_up(SEM_SYNC); + sem.post(); exit(0) } unsafe fn second() -> ! { println!("Second want to continue,but need to wait first"); - semaphore_down(SEM_SYNC); + sem.wait(); println!("Second can work now"); exit(0) } #[no_mangle] pub fn main() -> i32 { - // create semaphores - assert_eq!(semaphore_create(0) as usize, SEM_SYNC); // create threads let threads = vec![ thread_create(first as usize, 0), diff --git a/user/src/lang_items.rs b/user/src/lang_items.rs index df0467c33..899e02911 100644 --- a/user/src/lang_items.rs +++ b/user/src/lang_items.rs @@ -2,7 +2,7 @@ use super::{getpid, kill, SignalFlags}; #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { - let err = panic_info.message().unwrap(); + let err = panic_info.message(); if let Some(location) = panic_info.location() { println!( "Panicked at {}:{}, {}", diff --git a/user/src/lib.rs b/user/src/lib.rs index bbaee3677..791163b20 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -1,12 +1,12 @@ #![no_std] #![feature(linkage)] -#![feature(panic_info_message)] #![feature(alloc_error_handler)] #[macro_use] pub mod console; mod lang_items; mod syscall; +mod sync; extern crate alloc; #[macro_use] @@ -15,6 +15,7 @@ extern crate bitflags; use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; use syscall::*; +pub use sync::*; const USER_HEAP_SIZE: usize = 32768; @@ -33,7 +34,7 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { pub extern "C" fn _start(argc: usize, argv: usize) -> ! { unsafe { HEAP.lock() - .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); + .init(&raw mut HEAP_SPACE as usize, USER_HEAP_SIZE); } let mut v: Vec<&'static str> = Vec::new(); for i in 0..argc { @@ -167,35 +168,12 @@ pub fn waittid(tid: usize) -> isize { } } -pub fn mutex_create() -> isize { - sys_mutex_create(false) +pub fn futex_wait(flag_addr: *mut i32, expect: i32) -> isize { + sys_futex_wait(flag_addr, expect) } -pub fn mutex_blocking_create() -> isize { - sys_mutex_create(true) -} -pub fn mutex_lock(mutex_id: usize) { - sys_mutex_lock(mutex_id); -} -pub fn mutex_unlock(mutex_id: usize) { - sys_mutex_unlock(mutex_id); -} -pub fn semaphore_create(res_count: usize) -> isize { - sys_semaphore_create(res_count) -} -pub fn semaphore_up(sem_id: usize) { - sys_semaphore_up(sem_id); -} -pub fn semaphore_down(sem_id: usize) { - sys_semaphore_down(sem_id); -} -pub fn condvar_create() -> isize { - sys_condvar_create() -} -pub fn condvar_signal(condvar_id: usize) { - sys_condvar_signal(condvar_id); -} -pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { - sys_condvar_wait(condvar_id, mutex_id); + +pub fn futex_wake(flag_addr: *mut i32) -> isize { + sys_futex_wake(flag_addr) } #[macro_export] diff --git a/user/src/sync/condvar.rs b/user/src/sync/condvar.rs new file mode 100644 index 000000000..f8ab3654a --- /dev/null +++ b/user/src/sync/condvar.rs @@ -0,0 +1,32 @@ +use super::*; +use core::cell::UnsafeCell; +use crate::syscall::{sys_futex_wait, sys_futex_wake}; + +pub struct Condvar { + flag: UnsafeCell, +} + +unsafe impl Send for Condvar {} +unsafe impl Sync for Condvar {} + +impl Condvar { + pub fn new() -> Self { + Self { + flag: UnsafeCell::new(0) + } + } + + pub fn wait(&self, mutex: &MutexSpin) { + mutex.unlock(); + let addr = self.flag.get(); + let val = unsafe { *addr }; + sys_futex_wait(addr, val); + mutex.lock(); + } + + pub fn notify_one(&self) { + let addr = self.flag.get(); + atomic_increment(addr as *mut u32); + sys_futex_wake(addr); + } +} \ No newline at end of file diff --git a/user/src/sync/mod.rs b/user/src/sync/mod.rs new file mode 100644 index 000000000..6f5415157 --- /dev/null +++ b/user/src/sync/mod.rs @@ -0,0 +1,76 @@ +mod mutex; +mod condvar; +mod semaphore; + +pub use mutex::*; +pub use condvar::*; +pub use semaphore::*; + +#[inline(always)] +pub fn load_reserved(addr: *const u32) -> u32 { + let val; + unsafe { + core::arch::asm!( + "lr.w {}, ({})", + out(reg) val, + in(reg) addr + ); + } + val +} + +/// return true if successfully modify `addr` in memory +#[inline(always)] +pub fn store_conditional(addr: *mut u32, val: u32) -> bool { + let res: u32; + unsafe { + core::arch::asm!( + "sc.w {}, {}, ({})", + out(reg) res, + in(reg) val, + in(reg) addr + ); + } + res == 0 +} + +#[inline(always)] +pub fn atomic_increment(addr: *mut u32) { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val + 1) { break; } + } +} + +#[inline(always)] +pub fn atomic_decrement(addr: *mut u32) { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val - 1) { break; } + } +} + +/// 原子地测试 `*addr & (1 << bit)` 并设置 `(addr)` 为 `*addr | (1 << bit)`. +/// 返回测试结果,为 0 说明对应位原本是 0. +#[inline(always)] +pub fn atomic_test_and_set(addr: *mut u32, bit: u32) -> u32 { + loop { + let val = load_reserved(addr); + if store_conditional(addr, val | (1 << bit)) { + return val & (1 << bit); + } + } +} + +/// 原子地使 `(addr)` 自加 `addend`,并比较结果与 `expected` 是否相等。 +/// 相等则返回 `true` +#[inline(always)] +pub fn atomic_add_and_compare(addr: *mut u32, addend: u32, expected: u32) -> bool { + loop { + let val = load_reserved(addr); + let res = val + addend; + if store_conditional(addr, res) { + return res == expected + } + } +} \ No newline at end of file diff --git a/user/src/sync/mutex.rs b/user/src/sync/mutex.rs new file mode 100644 index 000000000..30961ac74 --- /dev/null +++ b/user/src/sync/mutex.rs @@ -0,0 +1,75 @@ +use super::*; +use core::cell::UnsafeCell; +use crate::syscall::{sys_futex_wait, sys_futex_wake}; + +pub struct MutexSpin { + locked: UnsafeCell, +} + +unsafe impl Send for MutexSpin {} +unsafe impl Sync for MutexSpin {} + +impl MutexSpin { + pub fn new() -> Self { + Self { + locked: UnsafeCell::new(0), + } + } + + pub fn lock(&self) { + let addr = self.locked.get(); + loop { + while load_reserved(addr) == 1 {} + if store_conditional(addr, 1) { return; } + } + } + + pub fn unlock(&self) { + let addr = self.locked.get(); + unsafe { *addr = 0; } + } +} + +pub struct Futex { + flag: UnsafeCell, +} + +unsafe impl Send for Futex {} +unsafe impl Sync for Futex {} + +impl Futex { + pub fn new() -> Self { + Self { + flag: UnsafeCell::new(0), + } + } + + pub fn lock(&self) { + let addr = self.flag.get(); + if atomic_test_and_set(addr as *mut u32, 31) == 0 { + // fastpath + return; + } + // one more waiter + atomic_increment(addr as *mut u32); + loop { + if atomic_test_and_set(addr as *mut u32, 31) == 0 { + atomic_decrement(addr as *mut u32); + return; + } + let flag = unsafe { *addr }; + if flag >= 0 { continue; } + // we have to wait now + sys_futex_wait(addr, flag); + } + } + + pub fn unlock(&self) { + let addr = self.flag.get(); + if atomic_add_and_compare(addr as *mut u32, 0x80000000, 0) { + // no threads are waiting + return; + } + sys_futex_wake(addr); + } +} \ No newline at end of file diff --git a/user/src/sync/semaphore.rs b/user/src/sync/semaphore.rs new file mode 100644 index 000000000..ebf9a53e1 --- /dev/null +++ b/user/src/sync/semaphore.rs @@ -0,0 +1,39 @@ +use super::*; +use core::cell::UnsafeCell; + +pub struct Semaphore { + count: UnsafeCell, + cond: Condvar, + mutex: MutexSpin, +} + +unsafe impl Send for Semaphore {} +unsafe impl Sync for Semaphore {} + +impl Semaphore { + pub fn new(count: i32) -> Self { + Self { + count: UnsafeCell::new(count), + cond: Condvar::new(), + mutex: MutexSpin::new(), + } + } + + pub fn wait(&self) { + self.mutex.lock(); + let cnt_p = self.count.get(); + while unsafe { *cnt_p <= 0 } { + self.cond.wait(&self.mutex); + } + unsafe { *cnt_p -= 1; } + self.mutex.unlock(); + } + + pub fn post(&self) { + self.mutex.lock(); + let cnt_p = self.count.get(); + unsafe { *cnt_p += 1; } + self.cond.notify_one(); + self.mutex.unlock(); + } +} \ No newline at end of file diff --git a/user/src/syscall.rs b/user/src/syscall.rs index eecdf0968..93efc5fc8 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -14,19 +14,15 @@ const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GETPID: usize = 172; const SYSCALL_FORK: usize = 220; const SYSCALL_EXEC: usize = 221; +const SYSCALL_FUTEX: usize = 240; 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; -const SYSCALL_SEMAPHORE_CREATE: usize = 1020; -const SYSCALL_SEMAPHORE_UP: usize = 1021; -const SYSCALL_SEMAPHORE_DOWN: usize = 1022; -const SYSCALL_CONDVAR_CREATE: usize = 1030; -const SYSCALL_CONDVAR_SIGNAL: usize = 1031; -const SYSCALL_CONDVAR_WAIT: usize = 1032; + +pub const FUTEX_WAIT: usize = 0; +pub const FUTEX_WAKE: usize = 1; + fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; @@ -121,38 +117,10 @@ pub fn sys_waittid(tid: usize) -> isize { syscall(SYSCALL_WAITTID, [tid, 0, 0]) } -pub fn sys_mutex_create(blocking: bool) -> isize { - syscall(SYSCALL_MUTEX_CREATE, [blocking as usize, 0, 0]) +pub fn sys_futex_wait(flag_addr: *mut i32, expect: i32) -> isize { + syscall(SYSCALL_FUTEX, [flag_addr as usize, FUTEX_WAIT, expect as usize]) } -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]) -} - -pub fn sys_semaphore_create(res_count: usize) -> isize { - syscall(SYSCALL_SEMAPHORE_CREATE, [res_count, 0, 0]) -} - -pub fn sys_semaphore_up(sem_id: usize) -> isize { - syscall(SYSCALL_SEMAPHORE_UP, [sem_id, 0, 0]) -} - -pub fn sys_semaphore_down(sem_id: usize) -> isize { - syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0]) -} - -pub fn sys_condvar_create() -> isize { - syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0]) -} - -pub fn sys_condvar_signal(condvar_id: usize) -> isize { - syscall(SYSCALL_CONDVAR_SIGNAL, [condvar_id, 0, 0]) -} - -pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { - syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0]) -} +pub fn sys_futex_wake(flag_addr: *mut i32) -> isize { + syscall(SYSCALL_FUTEX, [flag_addr as usize, FUTEX_WAKE, 0]) +} \ No newline at end of file