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();
}
/// 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,

@ -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<VecDeque<Arc<thread::Thread>>, 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() {

@ -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)
}

Loading…
Cancel
Save