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