Fix a potential racing in Condvar

master
Jiajie Chen 6 years ago
parent 3a6803af9b
commit adb66167f8

@ -113,6 +113,15 @@ pub fn park() {
processor().yield_now(); 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. /// A handle to a thread.
pub struct Thread { pub struct Thread {
tid: usize, tid: usize,

@ -2,6 +2,7 @@ use alloc::collections::VecDeque;
use super::*; use super::*;
use crate::thread; use crate::thread;
use alloc::sync::Arc; use alloc::sync::Arc;
use alloc::vec::Vec;
#[derive(Default)] #[derive(Default)]
pub struct Condvar { pub struct Condvar {
@ -13,21 +14,36 @@ impl Condvar {
Condvar::default() Condvar::default()
} }
/// Park current thread and wait for this condvar to be notified.
pub fn _wait(&self) { pub fn _wait(&self) {
self.add_to_wait_queue(); // The condvar might be notified between adding to queue and thread parking.
thread::park(); // 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]) { pub fn wait_any(condvars: &[&Condvar]) {
let token = Arc::new(thread::current()); 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 { 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) { pub fn add_to_wait_queue(&self) -> MutexGuard<VecDeque<Arc<thread::Thread>>, SpinNoIrq> {
self.wait_queue.lock().push_back(Arc::new(thread::current())); 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> 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. /// Notify up to `n` waiters.
/// Return the number of waiters that were woken up. /// 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; let mut count = 0;
while count < n { while count < n {
if let Some(t) = self.wait_queue.lock().pop_front() { if let Some(t) = self.wait_queue.lock().pop_front() {

@ -262,6 +262,7 @@ pub fn sys_nanosleep(req: *const TimeSpec) -> SysResult {
process().vm.check_read_ptr(req)?; process().vm.check_read_ptr(req)?;
let time = unsafe { req.read() }; let time = unsafe { req.read() };
info!("nanosleep: time: {:#?}", time); info!("nanosleep: time: {:#?}", time);
// TODO: handle spurious wakeup
thread::sleep(time.to_duration()); thread::sleep(time.to_duration());
Ok(0) Ok(0)
} }

Loading…
Cancel
Save