|
4 | 4 | //! threads, and are the building blocks of other concurrent
|
5 | 5 | //! types.
|
6 | 6 | //!
|
7 |
| -//! Rust atomics currently follow the same rules as [C++20 atomics][cpp], specifically `atomic_ref`. |
8 |
| -//! Basically, creating a *shared reference* to one of the Rust atomic types corresponds to creating |
9 |
| -//! an `atomic_ref` in C++; the `atomic_ref` is destroyed when the lifetime of the shared reference |
10 |
| -//! ends. (A Rust atomic type that is exclusively owned or behind a mutable reference does *not* |
11 |
| -//! correspond to an "atomic object" in C++, since it can be accessed via non-atomic operations.) |
12 |
| -//! |
13 | 7 | //! This module defines atomic versions of a select number of primitive
|
14 | 8 | //! types, including [`AtomicBool`], [`AtomicIsize`], [`AtomicUsize`],
|
15 | 9 | //! [`AtomicI8`], [`AtomicU16`], etc.
|
16 | 10 | //! Atomic types present operations that, when used correctly, synchronize
|
17 | 11 | //! updates between threads.
|
18 | 12 | //!
|
19 |
| -//! Each method takes an [`Ordering`] which represents the strength of |
20 |
| -//! the memory barrier for that operation. These orderings are the |
21 |
| -//! same as the [C++20 atomic orderings][1]. For more information see the [nomicon][2]. |
22 |
| -//! |
23 |
| -//! [cpp]: https://en.cppreference.com/w/cpp/atomic |
24 |
| -//! [1]: https://en.cppreference.com/w/cpp/atomic/memory_order |
25 |
| -//! [2]: ../../../nomicon/atomics.html |
26 |
| -//! |
27 | 13 | //! Atomic variables are safe to share between threads (they implement [`Sync`])
|
28 | 14 | //! but they do not themselves provide the mechanism for sharing and follow the
|
29 | 15 | //! [threading model](../../../std/thread/index.html#the-threading-model) of Rust.
|
|
36 | 22 | //! the constant initializers like [`AtomicBool::new`]. Atomic statics
|
37 | 23 | //! are often used for lazy global initialization.
|
38 | 24 | //!
|
| 25 | +//! ## Memory model for atomic accesses |
| 26 | +//! |
| 27 | +//! Rust atomics currently follow the same rules as [C++20 atomics][cpp], specifically `atomic_ref`. |
| 28 | +//! Basically, creating a *shared reference* to one of the Rust atomic types corresponds to creating |
| 29 | +//! an `atomic_ref` in C++; the `atomic_ref` is destroyed when the lifetime of the shared reference |
| 30 | +//! ends. (A Rust atomic type that is exclusively owned or behind a mutable reference does *not* |
| 31 | +//! correspond to an "atomic object" in C++, since it can be accessed via non-atomic operations.) |
| 32 | +//! |
| 33 | +//! [cpp]: https://en.cppreference.com/w/cpp/atomic |
| 34 | +//! |
| 35 | +//! Each method takes an [`Ordering`] which represents the strength of |
| 36 | +//! the memory barrier for that operation. These orderings are the |
| 37 | +//! same as the [C++20 atomic orderings][1]. For more information see the [nomicon][2]. |
| 38 | +//! |
| 39 | +//! [1]: https://en.cppreference.com/w/cpp/atomic/memory_order |
| 40 | +//! [2]: ../../../nomicon/atomics.html |
| 41 | +//! |
| 42 | +//! Since C++ does not support mixing atomic and non-atomic accesses, or non-synchronized |
| 43 | +//! different-sized accesses to the same data, Rust does not support those operations either. |
| 44 | +//! Note that both of those restrictions only apply if the accesses are non-synchronized. |
| 45 | +//! |
| 46 | +//! ```rust,no_run undefined_behavior |
| 47 | +//! use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; |
| 48 | +//! use std::mem::transmute; |
| 49 | +//! use std::thread; |
| 50 | +//! |
| 51 | +//! let atomic = AtomicU16::new(0); |
| 52 | +//! |
| 53 | +//! thread::scope(|s| { |
| 54 | +//! // This is UB: mixing atomic and non-atomic accesses |
| 55 | +//! s.spawn(|| atomic.store(1, Ordering::Relaxed)); |
| 56 | +//! s.spawn(|| unsafe { atomic.as_ptr().write(2) }); |
| 57 | +//! }); |
| 58 | +//! |
| 59 | +//! thread::scope(|s| { |
| 60 | +//! // This is UB: even reads are not allowed to be mixed |
| 61 | +//! s.spawn(|| atomic.load(Ordering::Relaxed)); |
| 62 | +//! s.spawn(|| unsafe { atomic.as_ptr().read() }); |
| 63 | +//! }); |
| 64 | +//! |
| 65 | +//! thread::scope(|s| { |
| 66 | +//! // This is fine, `join` synchronizes the code in a way such that atomic |
| 67 | +//! // and non-atomic accesses can't happen "at the same time" |
| 68 | +//! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); |
| 69 | +//! handle.join().unwrap(); |
| 70 | +//! s.spawn(|| unsafe { atomic.as_ptr().write(2) }); |
| 71 | +//! }); |
| 72 | +//! |
| 73 | +//! thread::scope(|s| { |
| 74 | +//! // This is UB: using different-sized atomic accesses to the same data |
| 75 | +//! s.spawn(|| atomic.store(1, Ordering::Relaxed)); |
| 76 | +//! s.spawn(|| unsafe { |
| 77 | +//! let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic); |
| 78 | +//! differently_sized.store(2, Ordering::Relaxed); |
| 79 | +//! }); |
| 80 | +//! }); |
| 81 | +//! |
| 82 | +//! thread::scope(|s| { |
| 83 | +//! // This is fine, `join` synchronizes the code in a way such that |
| 84 | +//! // differently-sized accesses can't happen "at the same time" |
| 85 | +//! let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); |
| 86 | +//! handle.join().unwrap(); |
| 87 | +//! s.spawn(|| unsafe { |
| 88 | +//! let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic); |
| 89 | +//! differently_sized.store(2, Ordering::Relaxed); |
| 90 | +//! }); |
| 91 | +//! }); |
| 92 | +//! ``` |
| 93 | +//! |
39 | 94 | //! # Portability
|
40 | 95 | //!
|
41 | 96 | //! All atomic types in this module are guaranteed to be [lock-free] if they're
|
@@ -383,16 +438,12 @@ impl AtomicBool {
|
383 | 438 | /// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can
|
384 | 439 | /// be bigger than `align_of::<bool>()`).
|
385 | 440 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
386 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship |
387 |
| - /// with atomic accesses via the returned value (or vice-versa). |
388 |
| - /// * In other words, time periods where the value is accessed atomically may not overlap |
389 |
| - /// with periods where the value is accessed non-atomically. |
390 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the |
391 |
| - /// duration of lifetime `'a`. Most use cases should be able to follow this guideline. |
392 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are done |
393 |
| - /// from the same thread. |
| 441 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 442 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 443 | + /// without synchronization. |
394 | 444 | ///
|
395 | 445 | /// [valid]: crate::ptr#safety
|
| 446 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
396 | 447 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
397 | 448 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
398 | 449 | pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool {
|
@@ -1185,18 +1236,12 @@ impl<T> AtomicPtr<T> {
|
1185 | 1236 | /// * `ptr` must be aligned to `align_of::<AtomicPtr<T>>()` (note that on some platforms this
|
1186 | 1237 | /// can be bigger than `align_of::<*mut T>()`).
|
1187 | 1238 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
1188 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship |
1189 |
| - /// with atomic accesses via the returned value (or vice-versa). |
1190 |
| - /// * In other words, time periods where the value is accessed atomically may not overlap |
1191 |
| - /// with periods where the value is accessed non-atomically. |
1192 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the |
1193 |
| - /// duration of lifetime `'a`. Most use cases should be able to follow this guideline. |
1194 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are done |
1195 |
| - /// from the same thread. |
1196 |
| - /// * This method should not be used to create overlapping or mixed-size atomic accesses, as |
1197 |
| - /// these are not supported by the memory model. |
| 1239 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 1240 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 1241 | + /// without synchronization. |
1198 | 1242 | ///
|
1199 | 1243 | /// [valid]: crate::ptr#safety
|
| 1244 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
1200 | 1245 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
1201 | 1246 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
1202 | 1247 | pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> {
|
@@ -2167,19 +2212,12 @@ macro_rules! atomic_int {
|
2167 | 2212 | `align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this \
|
2168 | 2213 | can be bigger than `align_of::<", stringify!($int_type), ">()`).")]
|
2169 | 2214 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
2170 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before |
2171 |
| - /// relationship with atomic accesses via the returned value (or vice-versa). |
2172 |
| - /// * In other words, time periods where the value is accessed atomically may not |
2173 |
| - /// overlap with periods where the value is accessed non-atomically. |
2174 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically |
2175 |
| - /// for the duration of lifetime `'a`. Most use cases should be able to follow |
2176 |
| - /// this guideline. |
2177 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are |
2178 |
| - /// done from the same thread. |
2179 |
| - /// * This method should not be used to create overlapping or mixed-size atomic |
2180 |
| - /// accesses, as these are not supported by the memory model. |
| 2215 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 2216 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 2217 | + /// without synchronization. |
2181 | 2218 | ///
|
2182 | 2219 | /// [valid]: crate::ptr#safety
|
| 2220 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
2183 | 2221 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
2184 | 2222 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
2185 | 2223 | pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type {
|
|
0 commit comments