fn main(){
static mut COUNTER:usize=0;
static mut TIMES:usize=0;
const TRY:usize=10000000;
let t1=thread::spawn(||{
for x in 0..TRY{
unsafe{
TIMES+=1;
//makes COUNTER keep changing between 0 and usize::MAX
if COUNTER==0{
COUNTER=usize::MAX;
}else{
COUNTER=0;
}
}
}
});
let t2=thread::spawn(||{
for x in 0..TRY{
unsafe{
let c=COUNTER;
if c != 0 && c != usize::MAX{
println!("Unexpected value: {}",c);
}
}
}
});
t1.join();
t2.join();
unsafe{
assert_eq!(TIMES,TRY);
}
}
I ran this code, no exception was found. But I wonder whether it is really safe or it just safe on my machine? My CPU architecture is aarch64.
I think if the length of bits of the value is modifying is less than the max bits CPU can process at one time, it is safe. Am I right?
Data races are undefined behavior in Rust by definition, so it's not safe. You can run Miri in the Playground and it will detect the UB in your example. (Miri is under Tools. I set TRY to something more reasonable.)
But playground is also worked well. I knew it maybe UB, but it actually worked "safely".
(Well, maybe TRY seems too scary, but it actually just costs few seconds to run this code on my machine )
If you try to run Miri with TRY set that high, I'm pretty sure the playground will always timeout. Miri is a few thousand times or so slower than compiled.
In the context of Rust, asking if some piece of unsafe code works safely will be taken to mean, is the code UB free. And the answer in this case is no.
You may be asking, is there some subset of UB where you can rely on the behavior anyway, as with some C compilers. And with rustc, the answer is always a very deliberate no.
At the language level, it's still a data race and UB. At the hardware level, the compiler is allowed to tear a read or write operation even if the hardware could have done it atomically. If you need atomic integer operations, use atomics to get defined behavior (even in the face of a race condition, which is distinct from a data race).
In 2021, the only correct usage of it is to machine-translate some single threaded C code into Rust syntax without modifying any semantics. You may never write any static mut by hand.