use enum for condvar locks

This commit is contained in:
DrMeepster 2022-10-30 14:56:49 -07:00
parent a1d94d43ac
commit a2f7e8497e
3 changed files with 76 additions and 44 deletions

View File

@ -116,15 +116,25 @@ struct RwLock {
declare_id!(CondvarId); declare_id!(CondvarId);
#[derive(Debug, Copy, Clone)]
pub enum RwLockMode {
Read,
Write,
}
#[derive(Debug)]
pub enum CondvarLock {
Mutex(MutexId),
RwLock { id: RwLockId, mode: RwLockMode },
}
/// A thread waiting on a conditional variable. /// A thread waiting on a conditional variable.
#[derive(Debug)] #[derive(Debug)]
struct CondvarWaiter { struct CondvarWaiter {
/// The thread that is waiting on this variable. /// The thread that is waiting on this variable.
thread: ThreadId, thread: ThreadId,
/// The mutex or rwlock on which the thread is waiting. /// The mutex or rwlock on which the thread is waiting.
lock: u32, lock: CondvarLock,
/// If the lock is shared or exclusive
shared: bool,
} }
/// The conditional variable state. /// The conditional variable state.
@ -571,16 +581,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
/// Mark that the thread is waiting on the conditional variable. /// Mark that the thread is waiting on the conditional variable.
fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: u32, shared: bool) { fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: CondvarLock) {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let waiters = &mut this.machine.threads.sync.condvars[id].waiters; let waiters = &mut this.machine.threads.sync.condvars[id].waiters;
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
waiters.push_back(CondvarWaiter { thread, lock, shared }); waiters.push_back(CondvarWaiter { thread, lock });
} }
/// Wake up some thread (if there is any) sleeping on the conditional /// Wake up some thread (if there is any) sleeping on the conditional
/// variable. /// variable.
fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, u32, bool)> { fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, CondvarLock)> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let current_thread = this.get_active_thread(); let current_thread = this.get_active_thread();
let condvar = &mut this.machine.threads.sync.condvars[id]; let condvar = &mut this.machine.threads.sync.condvars[id];
@ -594,7 +604,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
if let Some(data_race) = data_race { if let Some(data_race) = data_race {
data_race.validate_lock_acquire(&condvar.data_race, waiter.thread); data_race.validate_lock_acquire(&condvar.data_race, waiter.thread);
} }
(waiter.thread, waiter.lock, waiter.shared) (waiter.thread, waiter.lock)
}) })
} }

View File

@ -3,6 +3,7 @@ use std::time::SystemTime;
use rustc_hir::LangItem; use rustc_hir::LangItem;
use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, Ty}; use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, Ty};
use crate::concurrency::sync::CondvarLock;
use crate::concurrency::thread::{MachineCallback, Time}; use crate::concurrency::thread::{MachineCallback, Time};
use crate::*; use crate::*;
@ -696,9 +697,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
if let Some((thread, mutex, shared)) = this.condvar_signal(id) { if let Some((thread, lock)) = this.condvar_signal(id) {
assert!(!shared); if let CondvarLock::Mutex(mutex) = lock {
post_cond_signal(this, thread, MutexId::from_u32(mutex))?; post_cond_signal(this, thread, mutex)?;
} else {
panic!("condvar should not have an rwlock on unix");
}
} }
Ok(0) Ok(0)
@ -711,9 +715,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?; let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
while let Some((thread, mutex, shared)) = this.condvar_signal(id) { while let Some((thread, lock)) = this.condvar_signal(id) {
assert!(!shared); if let CondvarLock::Mutex(mutex) = lock {
post_cond_signal(this, thread, MutexId::from_u32(mutex))?; post_cond_signal(this, thread, mutex)?;
} else {
panic!("condvar should not have an rwlock on unix");
}
} }
Ok(0) Ok(0)
@ -731,7 +738,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let active_thread = this.get_active_thread(); let active_thread = this.get_active_thread();
release_cond_mutex_and_block(this, active_thread, mutex_id)?; release_cond_mutex_and_block(this, active_thread, mutex_id)?;
this.condvar_wait(id, active_thread, mutex_id.to_u32(), false); this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id));
Ok(0) Ok(0)
} }
@ -770,7 +777,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}; };
release_cond_mutex_and_block(this, active_thread, mutex_id)?; release_cond_mutex_and_block(this, active_thread, mutex_id)?;
this.condvar_wait(id, active_thread, mutex_id.to_u32(), false); this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id));
// We return success for now and override it in the timeout callback. // We return success for now and override it in the timeout callback.
this.write_scalar(Scalar::from_i32(0), dest)?; this.write_scalar(Scalar::from_i32(0), dest)?;

View File

@ -3,6 +3,7 @@ use std::time::Duration;
use rustc_target::abi::Size; use rustc_target::abi::Size;
use crate::concurrency::init_once::InitOnceStatus; use crate::concurrency::init_once::InitOnceStatus;
use crate::concurrency::sync::{CondvarLock, RwLockMode};
use crate::concurrency::thread::MachineCallback; use crate::concurrency::thread::MachineCallback;
use crate::*; use crate::*;
@ -18,23 +19,24 @@ pub trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tc
&mut self, &mut self,
thread: ThreadId, thread: ThreadId,
lock: RwLockId, lock: RwLockId,
shared: bool, mode: RwLockMode,
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.unblock_thread(thread); this.unblock_thread(thread);
if shared { match mode {
if this.rwlock_is_locked(lock) { RwLockMode::Read =>
this.rwlock_enqueue_and_block_reader(lock, thread); if this.rwlock_is_locked(lock) {
} else { this.rwlock_enqueue_and_block_reader(lock, thread);
this.rwlock_reader_lock(lock, thread); } else {
} this.rwlock_reader_lock(lock, thread);
} else { },
if this.rwlock_is_write_locked(lock) { RwLockMode::Write =>
this.rwlock_enqueue_and_block_writer(lock, thread); if this.rwlock_is_write_locked(lock) {
} else { this.rwlock_enqueue_and_block_writer(lock, thread);
this.rwlock_writer_lock(lock, thread); } else {
} this.rwlock_writer_lock(lock, thread);
},
} }
Ok(()) Ok(())
@ -383,14 +385,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}; };
let shared_mode = 0x1; // CONDITION_VARIABLE_LOCKMODE_SHARED is not in std let shared_mode = 0x1; // CONDITION_VARIABLE_LOCKMODE_SHARED is not in std
let shared = flags == shared_mode; let mode = if flags == 0 {
RwLockMode::Write
} else if flags == shared_mode {
RwLockMode::Read
} else {
throw_unsup_format!("unsupported `Flags` {flags} in `SleepConditionVariableSRW`");
};
let active_thread = this.get_active_thread(); let active_thread = this.get_active_thread();
let was_locked = if shared { let was_locked = match mode {
this.rwlock_reader_unlock(lock_id, active_thread) RwLockMode::Read => this.rwlock_reader_unlock(lock_id, active_thread),
} else { RwLockMode::Write => this.rwlock_writer_unlock(lock_id, active_thread),
this.rwlock_writer_unlock(lock_id, active_thread)
}; };
if !was_locked { if !was_locked {
@ -400,27 +407,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} }
this.block_thread(active_thread); this.block_thread(active_thread);
this.condvar_wait(condvar_id, active_thread, lock_id.to_u32(), shared); this.condvar_wait(condvar_id, active_thread, CondvarLock::RwLock { id: lock_id, mode });
if let Some(timeout_time) = timeout_time { if let Some(timeout_time) = timeout_time {
struct Callback<'tcx> { struct Callback<'tcx> {
thread: ThreadId, thread: ThreadId,
condvar_id: CondvarId, condvar_id: CondvarId,
lock_id: RwLockId, lock_id: RwLockId,
shared: bool, mode: RwLockMode,
dest: PlaceTy<'tcx, Provenance>, dest: PlaceTy<'tcx, Provenance>,
} }
impl<'tcx> VisitTags for Callback<'tcx> { impl<'tcx> VisitTags for Callback<'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) { fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Callback { thread: _, condvar_id: _, lock_id: _, shared: _, dest } = self; let Callback { thread: _, condvar_id: _, lock_id: _, mode: _, dest } = self;
dest.visit_tags(visit); dest.visit_tags(visit);
} }
} }
impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
this.reacquire_cond_lock(self.thread, self.lock_id, self.shared)?; this.reacquire_cond_lock(self.thread, self.lock_id, self.mode)?;
this.condvar_remove_waiter(self.condvar_id, self.thread); this.condvar_remove_waiter(self.condvar_id, self.thread);
@ -438,7 +445,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
thread: active_thread, thread: active_thread,
condvar_id, condvar_id,
lock_id, lock_id,
shared, mode,
dest: dest.clone(), dest: dest.clone(),
}), }),
); );
@ -451,9 +458,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?; let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?;
if let Some((thread, lock, shared)) = this.condvar_signal(condvar_id) { if let Some((thread, lock)) = this.condvar_signal(condvar_id) {
this.reacquire_cond_lock(thread, RwLockId::from_u32(lock), shared)?; if let CondvarLock::RwLock { id, mode } = lock {
this.unregister_timeout_callback_if_exists(thread); this.reacquire_cond_lock(thread, id, mode)?;
this.unregister_timeout_callback_if_exists(thread);
} else {
panic!("mutexes should not exist on windows");
}
} }
Ok(()) Ok(())
@ -466,9 +477,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?; let condvar_id = this.condvar_get_or_create_id(condvar_op, CONDVAR_ID_OFFSET)?;
while let Some((thread, lock, shared)) = this.condvar_signal(condvar_id) { while let Some((thread, lock)) = this.condvar_signal(condvar_id) {
this.reacquire_cond_lock(thread, RwLockId::from_u32(lock), shared)?; if let CondvarLock::RwLock { id, mode } = lock {
this.unregister_timeout_callback_if_exists(thread); this.reacquire_cond_lock(thread, id, mode)?;
this.unregister_timeout_callback_if_exists(thread);
} else {
panic!("mutexes should not exist on windows");
}
} }
Ok(()) Ok(())