|
2 | 2 |
|
3 | 3 | Safe Rust guarantees an absence of data races, which are defined as:
|
4 | 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 |
| 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 | 8 |
|
9 |
| -A data race has Undefined Behavior, and is therefore impossible to perform |
10 |
| -in Safe Rust. Data races are *mostly* prevented through Rust's ownership system: |
| 9 | +A data race has Undefined Behavior, and is therefore impossible to perform in |
| 10 | +Safe Rust. Data races are _mostly_ prevented through Rust's ownership system: |
11 | 11 | it's impossible to alias a mutable reference, so it's impossible to perform a
|
12 | 12 | data race. Interior mutability makes this more complicated, which is largely why
|
13 |
| -we have the Send and Sync traits (see below). |
| 13 | +we have the Send and Sync traits (see the next section for more on this). |
14 | 14 |
|
15 | 15 | **However Rust does not prevent general race conditions.**
|
16 | 16 |
|
17 |
| -This is pretty fundamentally impossible, and probably honestly undesirable. Your |
18 |
| -hardware is racy, your OS is racy, the other programs on your computer are racy, |
19 |
| -and the world this all runs in is racy. Any system that could genuinely claim to |
20 |
| -prevent *all* race conditions would be pretty awful to use, if not just |
21 |
| -incorrect. |
| 17 | +This is mathematically impossible in situations where you do not control the |
| 18 | +scheduler, which is true for the normal OS environment. If you do control |
| 19 | +preemption, it _can be_ possible to prevent general races - this technique is |
| 20 | +used by frameworks such as [RTIC](https://github.com/rtic-rs/rtic). However, |
| 21 | +actually having control over scheduling is a very uncommon case. |
22 | 22 |
|
23 |
| -So it's perfectly "fine" for a Safe Rust program to get deadlocked or do |
24 |
| -something nonsensical with incorrect synchronization. Obviously such a program |
25 |
| -isn't very good, but Rust can only hold your hand so far. Still, a race |
26 |
| -condition can't violate memory safety in a Rust program on its own. Only in |
27 |
| -conjunction with some other unsafe code can a race condition actually violate |
28 |
| -memory safety. For instance: |
| 23 | +For this reason, it is considered "safe" for Rust to get deadlocked or do |
| 24 | +something nonsensical with incorrect synchronization: this is known as a general |
| 25 | +race condition or resource race. Obviously such a program isn't very good, but |
| 26 | +Rust of course cannot prevent all logic errors. |
| 27 | + |
| 28 | +In any case, a race condition cannot violate memory safety in a Rust program on |
| 29 | +its own. Only in conjunction with some other unsafe code can a race condition |
| 30 | +actually violate memory safety. For instance, a correct program looks like this: |
29 | 31 |
|
30 | 32 | ```rust,no_run
|
31 | 33 | use std::thread;
|
@@ -58,6 +60,9 @@ thread::spawn(move || {
|
58 | 60 | println!("{}", data[idx.load(Ordering::SeqCst)]);
|
59 | 61 | ```
|
60 | 62 |
|
| 63 | +We can cause a data race if we instead do the bound check in advance, and then |
| 64 | +unsafely access the data with an unchecked value: |
| 65 | + |
61 | 66 | ```rust,no_run
|
62 | 67 | use std::thread;
|
63 | 68 | use std::sync::atomic::{AtomicUsize, Ordering};
|
|
0 commit comments