diff --git a/crate/thread/src/std_thread.rs b/crate/thread/src/std_thread.rs index fc9a246..db5c178 100644 --- a/crate/thread/src/std_thread.rs +++ b/crate/thread/src/std_thread.rs @@ -113,6 +113,15 @@ pub fn park() { processor().yield_now(); } +/// Blocks unless or until the current thread's token is made available. +/// Calls `f` before thread yields. Can be used to avoid racing. +pub fn park_action(f: impl FnOnce()) { + trace!("park:"); + processor().manager().sleep(current().id(), 0); + f(); + processor().yield_now(); +} + /// A handle to a thread. pub struct Thread { tid: usize, diff --git a/kernel/src/sync/condvar.rs b/kernel/src/sync/condvar.rs index e6f3f68..f04196b 100644 --- a/kernel/src/sync/condvar.rs +++ b/kernel/src/sync/condvar.rs @@ -2,6 +2,7 @@ use alloc::collections::VecDeque; use super::*; use crate::thread; use alloc::sync::Arc; +use alloc::vec::Vec; #[derive(Default)] pub struct Condvar { @@ -13,21 +14,36 @@ impl Condvar { Condvar::default() } + /// Park current thread and wait for this condvar to be notified. pub fn _wait(&self) { - self.add_to_wait_queue(); - thread::park(); + // The condvar might be notified between adding to queue and thread parking. + // So park current thread before wait queue lock is freed. + // Avoid racing + let lock = self.add_to_wait_queue(); + thread::park_action(move || { + drop(lock); + }); } pub fn wait_any(condvars: &[&Condvar]) { let token = Arc::new(thread::current()); + // Avoid racing in the same way as the function above + let mut locks = Vec::new(); + locks.reserve(condvars.len()); for condvar in condvars { - condvar.wait_queue.lock().push_back(token.clone()); + let mut lock = condvar.wait_queue.lock(); + lock.push_back(token.clone()); + locks.push(lock); } - thread::park(); + thread::park_action(move || { + drop(locks); + }); } - pub fn add_to_wait_queue(&self) { - self.wait_queue.lock().push_back(Arc::new(thread::current())); + pub fn add_to_wait_queue(&self) -> MutexGuard>, SpinNoIrq> { + let mut lock = self.wait_queue.lock(); + lock.push_back(Arc::new(thread::current())); + return lock; } pub fn wait<'a, T, S>(&self, guard: MutexGuard<'a, T, S>) -> MutexGuard<'a, T, S> @@ -51,7 +67,7 @@ impl Condvar { } /// Notify up to `n` waiters. /// Return the number of waiters that were woken up. - pub fn notify_n(&self, mut n: usize) -> usize { + pub fn notify_n(&self, n: usize) -> usize { let mut count = 0; while count < n { if let Some(t) = self.wait_queue.lock().pop_front() { diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs index 446805a..7182150 100644 --- a/kernel/src/syscall/proc.rs +++ b/kernel/src/syscall/proc.rs @@ -262,6 +262,7 @@ pub fn sys_nanosleep(req: *const TimeSpec) -> SysResult { process().vm.check_read_ptr(req)?; let time = unsafe { req.read() }; info!("nanosleep: time: {:#?}", time); + // TODO: handle spurious wakeup thread::sleep(time.to_duration()); Ok(0) }