1# Data Races and Race Conditions 2 3Safe Rust guarantees an absence of data races, which are defined as: 4 5* two or more threads concurrently accessing a location of memory 6* one or more of them is a write 7* one or more of them is unsynchronized 8 9A data race has Undefined Behavior, and is therefore impossible to perform 10in Safe Rust. Data races are *mostly* prevented through Rust's ownership system: 11it's impossible to alias a mutable reference, so it's impossible to perform a 12data race. Interior mutability makes this more complicated, which is largely why 13we have the Send and Sync traits (see below). 14 15**However Rust does not prevent general race conditions.** 16 17This is pretty fundamentally impossible, and probably honestly undesirable. Your 18hardware is racy, your OS is racy, the other programs on your computer are racy, 19and the world this all runs in is racy. Any system that could genuinely claim to 20prevent *all* race conditions would be pretty awful to use, if not just 21incorrect. 22 23So it's perfectly "fine" for a Safe Rust program to get deadlocked or do 24something nonsensical with incorrect synchronization. Obviously such a program 25isn't very good, but Rust can only hold your hand so far. Still, a race 26condition can't violate memory safety in a Rust program on its own. Only in 27conjunction with some other unsafe code can a race condition actually violate 28memory safety. For instance: 29 30```rust,no_run 31use std::thread; 32use std::sync::atomic::{AtomicUsize, Ordering}; 33use std::sync::Arc; 34 35let data = vec![1, 2, 3, 4]; 36// Arc so that the memory the AtomicUsize is stored in still exists for 37// the other thread to increment, even if we completely finish executing 38// before it. Rust won't compile the program without it, because of the 39// lifetime requirements of thread::spawn! 40let idx = Arc::new(AtomicUsize::new(0)); 41let other_idx = idx.clone(); 42 43// `move` captures other_idx by-value, moving it into this thread 44thread::spawn(move || { 45 // It's ok to mutate idx because this value 46 // is an atomic, so it can't cause a Data Race. 47 other_idx.fetch_add(10, Ordering::SeqCst); 48}); 49 50// Index with the value loaded from the atomic. This is safe because we 51// read the atomic memory only once, and then pass a copy of that value 52// to the Vec's indexing implementation. This indexing will be correctly 53// bounds checked, and there's no chance of the value getting changed 54// in the middle. However our program may panic if the thread we spawned 55// managed to increment before this ran. A race condition because correct 56// program execution (panicking is rarely correct) depends on order of 57// thread execution. 58println!("{}", data[idx.load(Ordering::SeqCst)]); 59``` 60 61```rust,no_run 62use std::thread; 63use std::sync::atomic::{AtomicUsize, Ordering}; 64use std::sync::Arc; 65 66let data = vec![1, 2, 3, 4]; 67 68let idx = Arc::new(AtomicUsize::new(0)); 69let other_idx = idx.clone(); 70 71// `move` captures other_idx by-value, moving it into this thread 72thread::spawn(move || { 73 // It's ok to mutate idx because this value 74 // is an atomic, so it can't cause a Data Race. 75 other_idx.fetch_add(10, Ordering::SeqCst); 76}); 77 78if idx.load(Ordering::SeqCst) < data.len() { 79 unsafe { 80 // Incorrectly loading the idx after we did the bounds check. 81 // It could have changed. This is a race condition, *and dangerous* 82 // because we decided to do `get_unchecked`, which is `unsafe`. 83 println!("{}", data.get_unchecked(idx.load(Ordering::SeqCst))); 84 } 85} 86``` 87