|
33 | 33 | //! atomic load (via the operations provided in this module). A "modification of an atomic object"
|
34 | 34 | //! refers to an atomic store.
|
35 | 35 | //!
|
36 |
| -//! The most important aspect of this model is that conflicting non-synchronized accesses are |
37 |
| -//! Undefined Behavior unless both accesses are atomic. Here, accesses are *conflicting* if they |
38 |
| -//! affect overlapping regions of memory and at least one of them is a write. They are |
39 |
| -//! *non-synchronized* if neither of them *happens-before* the other, according to the |
40 |
| -//! happens-before order of the memory model. |
41 |
| -//! |
42 | 36 | //! The end result is *almost* equivalent to saying that creating a *shared reference* to one of the
|
43 | 37 | //! Rust atomic types corresponds to creating an `atomic_ref` in C++, with the `atomic_ref` being
|
44 | 38 | //! destroyed when the lifetime of the shared reference ends. The main difference is that Rust
|
|
47 | 41 | //! objects" and "non-atomic objects" (with `atomic_ref` temporarily converting a non-atomic object
|
48 | 42 | //! into an atomic object).
|
49 | 43 | //!
|
50 |
| -//! That said, Rust *does* inherit the C++ limitation that non-synchronized conflicting atomic |
51 |
| -//! accesses may not partially overlap: they must be either disjoint or access the exact same |
52 |
| -//! memory. This in particular rules out non-synchronized differently-sized atomic accesses to the |
53 |
| -//! same data unless all accesses are reads. |
| 44 | +//! The most important aspect of this model is that *data races* are undefined behavior. A data race |
| 45 | +//! is defined as conflicting non-synchronized accesses where at least one of the accesses is |
| 46 | +//! non-atomic. Here, accesses are *conflicting* if they affect overlapping regions of memory and at |
| 47 | +//! least one of them is a write. They are *non-synchronized* if neither of them *happens-before* |
| 48 | +//! the other, according to the happens-before order of the memory model. |
54 | 49 | //!
|
55 |
| -//! [cpp]: https://en.cppreference.com/w/cpp/atomic |
56 |
| -//! [cpp-intro.races]: https://timsong-cpp.github.io/cppwp/n4868/intro.multithread#intro.races |
| 50 | +//! The other possible cause of undefined behavior in the memory model are mixed-size accesses: Rust |
| 51 | +//! inherits the C++ limitation that non-synchronized conflicting atomic accesses may not partially |
| 52 | +//! overlap. In other words, every pair of non-synchronized atomic accesses must be either disjoint, |
| 53 | +//! access the exact same memory (including using the same access size), or both be reads. |
57 | 54 | //!
|
58 |
| -//! Each method takes an [`Ordering`] which represents the strength of |
59 |
| -//! the memory barrier for that operation. These orderings behave the |
60 |
| -//! same as the corresponding [C++20 atomic orderings][1]. For more information see the [nomicon][2]. |
| 55 | +//! Each atomic access takes an [`Ordering`] which defines how the operation interacts with the |
| 56 | +//! happens-before order. These orderings behave the same as the corresponding [C++20 atomic |
| 57 | +//! orderings][cpp_memory_order]. For more information, see the [nomicon]. |
61 | 58 | //!
|
62 |
| -//! [1]: https://en.cppreference.com/w/cpp/atomic/memory_order |
63 |
| -//! [2]: ../../../nomicon/atomics.html |
| 59 | +//! [cpp]: https://en.cppreference.com/w/cpp/atomic |
| 60 | +//! [cpp-intro.races]: https://timsong-cpp.github.io/cppwp/n4868/intro.multithread#intro.races |
| 61 | +//! [cpp_memory_order]: https://en.cppreference.com/w/cpp/atomic/memory_order |
| 62 | +//! [nomicon]: ../../../nomicon/atomics.html |
64 | 63 | //!
|
65 | 64 | //! ```rust,no_run undefined_behavior
|
66 | 65 | //! use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
|
|
157 | 156 | //!
|
158 | 157 | //! # Atomic accesses to read-only memory
|
159 | 158 | //!
|
160 |
| -//! In general, *all* atomic accesses on read-only memory are Undefined Behavior. For instance, attempting |
| 159 | +//! In general, *all* atomic accesses on read-only memory are undefined behavior. For instance, attempting |
161 | 160 | //! to do a `compare_exchange` that will definitely fail (making it conceptually a read-only
|
162 | 161 | //! operation) can still cause a segmentation fault if the underlying memory page is mapped read-only. Since
|
163 | 162 | //! atomic `load`s might be implemented using compare-exchange operations, even a `load` can fault
|
|
173 | 172 | //!
|
174 | 173 | //! As an exception from the general rule stated above, "sufficiently small" atomic loads with
|
175 | 174 | //! `Ordering::Relaxed` are implemented in a way that works on read-only memory, and are hence not
|
176 |
| -//! Undefined Behavior. The exact size limit for what makes a load "sufficiently small" varies |
| 175 | +//! undefined behavior. The exact size limit for what makes a load "sufficiently small" varies |
177 | 176 | //! depending on the target:
|
178 | 177 | //!
|
179 | 178 | //! | `target_arch` | Size limit |
|
|
0 commit comments