fix shared behavior and add tests
This commit is contained in:
parent
a2f7e8497e
commit
2eb07a05a3
@ -12,7 +12,7 @@ const INIT_ONCE_ID_OFFSET: u64 = 0;
|
|||||||
const CONDVAR_ID_OFFSET: u64 = 0;
|
const CONDVAR_ID_OFFSET: u64 = 0;
|
||||||
|
|
||||||
impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
|
impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
|
||||||
pub trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
/// Try to reacquire the lock associated with the condition variable after we
|
/// Try to reacquire the lock associated with the condition variable after we
|
||||||
/// were signaled.
|
/// were signaled.
|
||||||
fn reacquire_cond_lock(
|
fn reacquire_cond_lock(
|
||||||
@ -26,13 +26,13 @@ pub trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tc
|
|||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
RwLockMode::Read =>
|
RwLockMode::Read =>
|
||||||
if this.rwlock_is_locked(lock) {
|
if this.rwlock_is_write_locked(lock) {
|
||||||
this.rwlock_enqueue_and_block_reader(lock, thread);
|
this.rwlock_enqueue_and_block_reader(lock, thread);
|
||||||
} else {
|
} else {
|
||||||
this.rwlock_reader_lock(lock, thread);
|
this.rwlock_reader_lock(lock, thread);
|
||||||
},
|
},
|
||||||
RwLockMode::Write =>
|
RwLockMode::Write =>
|
||||||
if this.rwlock_is_write_locked(lock) {
|
if this.rwlock_is_locked(lock) {
|
||||||
this.rwlock_enqueue_and_block_writer(lock, thread);
|
this.rwlock_enqueue_and_block_writer(lock, thread);
|
||||||
} else {
|
} else {
|
||||||
this.rwlock_writer_lock(lock, thread);
|
this.rwlock_writer_lock(lock, thread);
|
||||||
|
227
src/tools/miri/tests/pass/concurrency/windows_condvar_shared.rs
Normal file
227
src/tools/miri/tests/pass/concurrency/windows_condvar_shared.rs
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
//@only-target-windows: Uses win32 api functions
|
||||||
|
// We are making scheduler assumptions here.
|
||||||
|
//@compile-flags: -Zmiri-preemption-rate=0
|
||||||
|
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct SendPtr<T>(*mut T);
|
||||||
|
|
||||||
|
unsafe impl<T> Send for SendPtr<T> {}
|
||||||
|
|
||||||
|
extern "system" {
|
||||||
|
fn SleepConditionVariableSRW(
|
||||||
|
condvar: *mut *mut c_void,
|
||||||
|
lock: *mut *mut c_void,
|
||||||
|
timeout: u32,
|
||||||
|
flags: u32,
|
||||||
|
) -> i32;
|
||||||
|
fn WakeAllConditionVariable(condvar: *mut *mut c_void);
|
||||||
|
|
||||||
|
fn AcquireSRWLockExclusive(lock: *mut *mut c_void);
|
||||||
|
fn AcquireSRWLockShared(lock: *mut *mut c_void);
|
||||||
|
fn ReleaseSRWLockExclusive(lock: *mut *mut c_void);
|
||||||
|
fn ReleaseSRWLockShared(lock: *mut *mut c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONDITION_VARIABLE_LOCKMODE_SHARED: u32 = 1;
|
||||||
|
const INFINITE: u32 = u32::MAX;
|
||||||
|
|
||||||
|
/// threads should be able to reacquire the lock while it is locked by multiple other threads in shared mode
|
||||||
|
fn all_shared() {
|
||||||
|
println!("all_shared");
|
||||||
|
|
||||||
|
let mut lock = null_mut();
|
||||||
|
let mut condvar = null_mut();
|
||||||
|
|
||||||
|
let lock_ptr = SendPtr(&mut lock);
|
||||||
|
let condvar_ptr = SendPtr(&mut condvar);
|
||||||
|
|
||||||
|
let mut handles = Vec::with_capacity(10);
|
||||||
|
|
||||||
|
// waiters
|
||||||
|
for i in 0..5 {
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("exclusive waiter {i} locked");
|
||||||
|
|
||||||
|
let r = unsafe {
|
||||||
|
SleepConditionVariableSRW(
|
||||||
|
condvar_ptr.0,
|
||||||
|
lock_ptr.0,
|
||||||
|
INFINITE,
|
||||||
|
CONDITION_VARIABLE_LOCKMODE_SHARED,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert_ne!(r, 0);
|
||||||
|
|
||||||
|
println!("exclusive waiter {i} reacquired lock");
|
||||||
|
|
||||||
|
// unlocking is unnecessary because the lock is never used again
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensures each waiter is waiting by this point
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
// readers
|
||||||
|
for i in 0..5 {
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("reader {i} locked");
|
||||||
|
|
||||||
|
// switch to next reader or main thread
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ReleaseSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("reader {i} unlocked");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensures each reader has acquired the lock
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
WakeAllConditionVariable(condvar_ptr.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reacquiring a lock should wait until the lock is not exclusively locked
|
||||||
|
fn shared_sleep_and_exclusive_lock() {
|
||||||
|
println!("shared_sleep_and_exclusive_lock");
|
||||||
|
|
||||||
|
let mut lock = null_mut();
|
||||||
|
let mut condvar = null_mut();
|
||||||
|
|
||||||
|
let lock_ptr = SendPtr(&mut lock);
|
||||||
|
let condvar_ptr = SendPtr(&mut condvar);
|
||||||
|
|
||||||
|
let mut waiters = Vec::with_capacity(5);
|
||||||
|
for i in 0..5 {
|
||||||
|
waiters.push(thread::spawn(move || {
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("shared waiter {i} locked");
|
||||||
|
|
||||||
|
let r = unsafe {
|
||||||
|
SleepConditionVariableSRW(
|
||||||
|
condvar_ptr.0,
|
||||||
|
lock_ptr.0,
|
||||||
|
INFINITE,
|
||||||
|
CONDITION_VARIABLE_LOCKMODE_SHARED,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert_ne!(r, 0);
|
||||||
|
|
||||||
|
println!("shared waiter {i} reacquired lock");
|
||||||
|
|
||||||
|
// unlocking is unnecessary because the lock is never used again
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensures each waiter is waiting by this point
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockExclusive(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("main locked");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
WakeAllConditionVariable(condvar_ptr.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// waiters are now waiting for the lock to be unlocked
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ReleaseSRWLockExclusive(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("main unlocked");
|
||||||
|
|
||||||
|
for handle in waiters {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// threads reacquiring locks should wait for all locks to be released first
|
||||||
|
fn exclusive_sleep_and_shared_lock() {
|
||||||
|
println!("exclusive_sleep_and_shared_lock");
|
||||||
|
|
||||||
|
let mut lock = null_mut();
|
||||||
|
let mut condvar = null_mut();
|
||||||
|
|
||||||
|
let lock_ptr = SendPtr(&mut lock);
|
||||||
|
let condvar_ptr = SendPtr(&mut condvar);
|
||||||
|
|
||||||
|
let mut handles = Vec::with_capacity(10);
|
||||||
|
for i in 0..5 {
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockExclusive(lock_ptr.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("exclusive waiter {i} locked");
|
||||||
|
|
||||||
|
let r = unsafe { SleepConditionVariableSRW(condvar_ptr.0, lock_ptr.0, INFINITE, 0) };
|
||||||
|
assert_ne!(r, 0);
|
||||||
|
|
||||||
|
println!("exclusive waiter {i} reacquired lock");
|
||||||
|
|
||||||
|
// switch to next waiter or main thread
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ReleaseSRWLockExclusive(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("exclusive waiter {i} unlocked");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..5 {
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
unsafe {
|
||||||
|
AcquireSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("reader {i} locked");
|
||||||
|
|
||||||
|
// switch to next reader or main thread
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ReleaseSRWLockShared(lock_ptr.0);
|
||||||
|
}
|
||||||
|
println!("reader {i} unlocked");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensures each reader has acquired the lock
|
||||||
|
thread::yield_now();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
WakeAllConditionVariable(condvar_ptr.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for handle in handles {
|
||||||
|
handle.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
all_shared();
|
||||||
|
shared_sleep_and_exclusive_lock();
|
||||||
|
exclusive_sleep_and_shared_lock();
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
all_shared
|
||||||
|
exclusive waiter 0 locked
|
||||||
|
exclusive waiter 1 locked
|
||||||
|
exclusive waiter 2 locked
|
||||||
|
exclusive waiter 3 locked
|
||||||
|
exclusive waiter 4 locked
|
||||||
|
reader 0 locked
|
||||||
|
reader 1 locked
|
||||||
|
reader 2 locked
|
||||||
|
reader 3 locked
|
||||||
|
reader 4 locked
|
||||||
|
exclusive waiter 0 reacquired lock
|
||||||
|
exclusive waiter 1 reacquired lock
|
||||||
|
exclusive waiter 2 reacquired lock
|
||||||
|
exclusive waiter 3 reacquired lock
|
||||||
|
exclusive waiter 4 reacquired lock
|
||||||
|
reader 0 unlocked
|
||||||
|
reader 1 unlocked
|
||||||
|
reader 2 unlocked
|
||||||
|
reader 3 unlocked
|
||||||
|
reader 4 unlocked
|
||||||
|
shared_sleep_and_exclusive_lock
|
||||||
|
shared waiter 0 locked
|
||||||
|
shared waiter 1 locked
|
||||||
|
shared waiter 2 locked
|
||||||
|
shared waiter 3 locked
|
||||||
|
shared waiter 4 locked
|
||||||
|
main locked
|
||||||
|
main unlocked
|
||||||
|
shared waiter 0 reacquired lock
|
||||||
|
shared waiter 1 reacquired lock
|
||||||
|
shared waiter 2 reacquired lock
|
||||||
|
shared waiter 3 reacquired lock
|
||||||
|
shared waiter 4 reacquired lock
|
||||||
|
exclusive_sleep_and_shared_lock
|
||||||
|
exclusive waiter 0 locked
|
||||||
|
exclusive waiter 1 locked
|
||||||
|
exclusive waiter 2 locked
|
||||||
|
exclusive waiter 3 locked
|
||||||
|
exclusive waiter 4 locked
|
||||||
|
reader 0 locked
|
||||||
|
reader 1 locked
|
||||||
|
reader 2 locked
|
||||||
|
reader 3 locked
|
||||||
|
reader 4 locked
|
||||||
|
reader 0 unlocked
|
||||||
|
reader 1 unlocked
|
||||||
|
reader 2 unlocked
|
||||||
|
reader 3 unlocked
|
||||||
|
reader 4 unlocked
|
||||||
|
exclusive waiter 0 reacquired lock
|
||||||
|
exclusive waiter 0 unlocked
|
||||||
|
exclusive waiter 1 reacquired lock
|
||||||
|
exclusive waiter 1 unlocked
|
||||||
|
exclusive waiter 2 reacquired lock
|
||||||
|
exclusive waiter 2 unlocked
|
||||||
|
exclusive waiter 3 reacquired lock
|
||||||
|
exclusive waiter 3 unlocked
|
||||||
|
exclusive waiter 4 reacquired lock
|
||||||
|
exclusive waiter 4 unlocked
|
Loading…
x
Reference in New Issue
Block a user