Created
December 5, 2020 17:52
-
-
Save Badel2/417cee6c989834ff43f0a30270d55d40 to your computer and use it in GitHub Desktop.
Using miri to detect data races
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Using miri to detect data races. | |
//! | |
//! A data race ocurrs when a variable is shared by multiple threads | |
//! without any synchronization primitives, and at least one of the threads may | |
//! mutate the variable. Reading or writing while the variable is being updated | |
//! is undefined behavior. | |
//! | |
//! Note that miri will only detect data races that are ocurring during the | |
//! current execution. It will not formally prove that your program is free of | |
//! data races. | |
//! | |
//! Keep in mind that a data race is not the same as a race condition. | |
//! Race conditions are logic errors and are possible in safe Rust. | |
use std::sync::Arc; | |
use std::sync::Mutex; | |
// This global variable will be updated by two different threads. | |
// This is undefined behavior: a variable of type `usize` cannot be safely | |
// updated while it is being accessed by other threads. | |
// A counter like this should be an `AtomicUsize`. | |
static mut COUNTER: usize = 0; | |
fn main() { | |
// Instead of an `AtomicUsize`, we can also use a mutex to ensure | |
// synchronized access to the global COUNTER. But note that this makes the | |
// code harder to follow. | |
let mutex = Arc::new(Mutex::new(())); | |
// Spawn thread which will increment the counter by 1000 | |
let handle = std::thread::spawn({ | |
let mutex = Arc::clone(&mutex); | |
move || { | |
// Lock the mutex before accessing the global variable. | |
let _handle = mutex.lock(); | |
// We need to use an unsafe block to access the global variable: | |
// use of mutable static is unsafe | |
// = note: mutable statics can be mutated by multiple threads: | |
// aliasing violations or data races will cause undefined behavior | |
unsafe { | |
COUNTER += 1000; | |
println!("other thread: {}", COUNTER); | |
} | |
// The handle is dropped when it goes out of scope, allowing other | |
// threads to safely access the global COUNTER | |
} | |
}); | |
// Pause the main thread and allow the other thread to go first | |
std::thread::sleep(std::time::Duration::from_millis(1)); | |
unsafe { | |
// Uncomment this line to fix data race: | |
//let _handle = mutex.lock(); | |
COUNTER += 1; | |
println!("main thread: {}", COUNTER); | |
} | |
handle.join().expect("thread failed"); | |
unsafe { | |
// This is an unsynchronized access, but it is not a data race because | |
// the other thread has already finished | |
assert_eq!(COUNTER, 1001); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment