Skip to content

Commit

Permalink
Add tests, and fix bug in atomic RMW relaxed stores
Browse files Browse the repository at this point in the history
  • Loading branch information
JCTyblaidd committed Nov 2, 2020
1 parent e4a9192 commit 837715a
Show file tree
Hide file tree
Showing 16 changed files with 315 additions and 11 deletions.
5 changes: 3 additions & 2 deletions src/data_race.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
3 changes: 1 addition & 2 deletions src/shims/posix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions tests/compile-fail/data_race/read_write_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 42 additions & 0 deletions tests/compile-fail/data_race/relax_acquire_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions tests/compile-fail/data_race/release_seq_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 43 additions & 0 deletions tests/compile-fail/data_race/rmw_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions tests/compile-fail/data_race/write_write_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 119 additions & 0 deletions tests/run-pass/concurrency/data_race.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 2 additions & 0 deletions tests/run-pass/concurrency/data_race.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/concurrency/linux-futex.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/concurrency/simple.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/run-pass/concurrency/sync.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/concurrency/thread_locals.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/concurrency/tls_lib_drop.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/libc.stderr
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
warning: thread support is experimental. For example, Miri does not detect data races yet.
warning: thread support is experimental.

2 changes: 1 addition & 1 deletion tests/run-pass/panic/concurrent-panic.stderr
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 837715a

Please sign in to comment.