Add tests, and fix bug in atomic RMW relaxed stores
This commit is contained in:
parent
89814f1b3f
commit
ed32b260f9
16 changed files with 315 additions and 11 deletions
|
|
@ -404,8 +404,9 @@ impl AtomicReleaseSequences {
|
|||
fn clear_and_retain(&mut self, thread: ThreadId) {
|
||||
match self {
|
||||
Self::ReleaseOneOrEmpty(id, rel_clock) => {
|
||||
// Keep or forget depending on id
|
||||
if *id == Some(thread) {
|
||||
// If the id is the same, then reatin the value
|
||||
// otherwise delete and clear the release vector clock
|
||||
if *id != Some(thread) {
|
||||
*id = None;
|
||||
rel_clock.set_zero_vector();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
let this = self.eval_context_mut();
|
||||
|
||||
this.tcx.sess.warn(
|
||||
"thread support is experimental. \
|
||||
For example, Miri does not detect data races yet.",
|
||||
"thread support is experimental.",
|
||||
);
|
||||
|
||||
// Create the new thread
|
||||
|
|
|
|||
26
tests/compile-fail/data_race/read_write_race.rs
Normal file
26
tests/compile-fail/data_race/read_write_race.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
use std::thread::spawn;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
pub fn main() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
*c.0 = 64; //~ ERROR Data race
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
}
|
||||
}
|
||||
42
tests/compile-fail/data_race/relax_acquire_race.rs
Normal file
42
tests/compile-fail/data_race/relax_acquire_race.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
use std::thread::spawn;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
static SYNC: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn main() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 1;
|
||||
SYNC.store(1, Ordering::Release);
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
if SYNC.load(Ordering::Acquire) == 1 {
|
||||
SYNC.store(2, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
|
||||
let j3 = spawn(move || {
|
||||
if SYNC.load(Ordering::Acquire) == 2 {
|
||||
*c.0 //~ ERROR Data race
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
j3.join().unwrap();
|
||||
}
|
||||
}
|
||||
46
tests/compile-fail/data_race/release_seq_race.rs
Normal file
46
tests/compile-fail/data_race/release_seq_race.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
use std::thread::{spawn, sleep};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
static SYNC: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn main() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 1;
|
||||
SYNC.store(1, Ordering::Release);
|
||||
sleep(Duration::from_millis(100));
|
||||
SYNC.store(3, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
// Blocks the acquire-release sequence
|
||||
SYNC.store(2, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
let j3 = spawn(move || {
|
||||
sleep(Duration::from_millis(1000));
|
||||
if SYNC.load(Ordering::Acquire) == 3 {
|
||||
*c.0 //~ ERROR Data race
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
j3.join().unwrap();
|
||||
}
|
||||
}
|
||||
43
tests/compile-fail/data_race/rmw_race.rs
Normal file
43
tests/compile-fail/data_race/rmw_race.rs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
use std::thread::spawn;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
static SYNC: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn main() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 1;
|
||||
SYNC.store(1, Ordering::Release);
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
if SYNC.swap(2, Ordering::Relaxed) == 1 {
|
||||
// Blocks the acquire-release sequence
|
||||
SYNC.store(3, Ordering::Relaxed);
|
||||
}
|
||||
});
|
||||
|
||||
let j3 = spawn(move || {
|
||||
if SYNC.load(Ordering::Acquire) == 3 {
|
||||
*c.0 //~ ERROR Data race
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
j3.join().unwrap();
|
||||
}
|
||||
}
|
||||
26
tests/compile-fail/data_race/write_write_race.rs
Normal file
26
tests/compile-fail/data_race/write_write_race.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
use std::thread::spawn;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
pub fn main() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 32;
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
*c.0 = 64; //~ ERROR Data race
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
}
|
||||
}
|
||||
119
tests/run-pass/concurrency/data_race.rs
Normal file
119
tests/run-pass/concurrency/data_race.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use std::sync::atomic::{AtomicUsize, fence, Ordering};
|
||||
use std::thread::spawn;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct EvilSend<T>(pub T);
|
||||
|
||||
unsafe impl<T> Send for EvilSend<T> {}
|
||||
unsafe impl<T> Sync for EvilSend<T> {}
|
||||
|
||||
static SYNC: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
fn test_fence_sync() {
|
||||
let mut var = 0u32;
|
||||
let ptr = &mut var as *mut u32;
|
||||
let evil_ptr = EvilSend(ptr);
|
||||
|
||||
|
||||
let j1 = spawn(move || {
|
||||
unsafe { *evil_ptr.0 = 1; }
|
||||
fence(Ordering::Release);
|
||||
SYNC.store(1, Ordering::Relaxed)
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
if SYNC.load(Ordering::Relaxed) == 1 {
|
||||
fence(Ordering::Acquire);
|
||||
unsafe { *evil_ptr.0 }
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
}
|
||||
|
||||
|
||||
fn test_multiple_reads() {
|
||||
let mut var = 42u32;
|
||||
let ptr = &mut var as *mut u32;
|
||||
let evil_ptr = EvilSend(ptr);
|
||||
|
||||
let j1 = spawn(move || unsafe {*evil_ptr.0});
|
||||
let j2 = spawn(move || unsafe {*evil_ptr.0});
|
||||
let j3 = spawn(move || unsafe {*evil_ptr.0});
|
||||
let j4 = spawn(move || unsafe {*evil_ptr.0});
|
||||
|
||||
assert_eq!(j1.join().unwrap(), 42);
|
||||
assert_eq!(j2.join().unwrap(), 42);
|
||||
assert_eq!(j3.join().unwrap(), 42);
|
||||
assert_eq!(j4.join().unwrap(), 42);
|
||||
|
||||
var = 10;
|
||||
assert_eq!(var, 10);
|
||||
}
|
||||
|
||||
pub fn test_rmw_no_block() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 1;
|
||||
SYNC.store(1, Ordering::Release);
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
if SYNC.swap(2, Ordering::Relaxed) == 1 {
|
||||
//No op, blocking store removed
|
||||
}
|
||||
});
|
||||
|
||||
let j3 = spawn(move || {
|
||||
if SYNC.load(Ordering::Acquire) == 2 {
|
||||
*c.0
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
j2.join().unwrap();
|
||||
let v = j3.join().unwrap();
|
||||
assert!(v == 1 || v == 2);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_release_no_block() {
|
||||
let mut a = 0u32;
|
||||
let b = &mut a as *mut u32;
|
||||
let c = EvilSend(b);
|
||||
|
||||
unsafe {
|
||||
let j1 = spawn(move || {
|
||||
*c.0 = 1;
|
||||
SYNC.store(1, Ordering::Release);
|
||||
SYNC.store(3, Ordering::Relaxed);
|
||||
});
|
||||
|
||||
let j2 = spawn(move || {
|
||||
if SYNC.load(Ordering::Acquire) == 3 {
|
||||
*c.0
|
||||
}else{
|
||||
0
|
||||
}
|
||||
});
|
||||
|
||||
j1.join().unwrap();
|
||||
assert_eq!(j2.join().unwrap(),1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
test_fence_sync();
|
||||
test_multiple_reads();
|
||||
test_rmw_no_block();
|
||||
test_release_no_block();
|
||||
}
|
||||
2
tests/run-pass/concurrency/data_race.stderr
Normal file
2
tests/run-pass/concurrency/data_race.stderr
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
warning: thread support is experimental.
|
||||
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
thread '<unnamed>' panicked at 'Hello!', $DIR/simple.rs:54:9
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
warning: thread support is experimental. For example, Miri does not detect data races yet.
|
||||
warning: thread support is experimental.
|
||||
|
||||
Thread 1 starting, will block on mutex
|
||||
Thread 1 reported it has started
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue