Skip to content

Commit 0e58906

Browse files
committed
Reword the section on general race conditions
The section on preventing general race conditions is a bit hand wavy. Change wording to be more concrete, and add an example of Rust preventing general races in a very specific case.
1 parent 1842257 commit 0e58906

File tree

1 file changed

+19
-14
lines changed

1 file changed

+19
-14
lines changed

src/races.md

+19-14
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,28 @@ Safe Rust guarantees an absence of data races, which are defined as:
66
* one or more of them is a write
77
* one or more of them is unsynchronized
88

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:
1111
it's impossible to alias a mutable reference, so it's impossible to perform a
1212
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).
1414

1515
**However Rust does not prevent general race conditions.**
1616

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.
2222

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:
2931

3032
```rust,no_run
3133
use std::thread;
@@ -58,6 +60,9 @@ thread::spawn(move || {
5860
println!("{}", data[idx.load(Ordering::SeqCst)]);
5961
```
6062

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+
6166
```rust,no_run
6267
use std::thread;
6368
use std::sync::atomic::{AtomicUsize, Ordering};

0 commit comments

Comments
 (0)