|
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
|
@@ -349,16 +404,12 @@ impl AtomicBool {
|
349 | 404 | /// * `ptr` must be aligned to `align_of::<AtomicBool>()` (note that on some platforms this can
|
350 | 405 | /// be bigger than `align_of::<bool>()`).
|
351 | 406 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
352 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship |
353 |
| - /// with atomic accesses via the returned value (or vice-versa). |
354 |
| - /// * In other words, time periods where the value is accessed atomically may not overlap |
355 |
| - /// with periods where the value is accessed non-atomically. |
356 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the |
357 |
| - /// duration of lifetime `'a`. Most use cases should be able to follow this guideline. |
358 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are done |
359 |
| - /// from the same thread. |
| 407 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 408 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 409 | + /// without synchronization. |
360 | 410 | ///
|
361 | 411 | /// [valid]: crate::ptr#safety
|
| 412 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
362 | 413 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
363 | 414 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
364 | 415 | pub const unsafe fn from_ptr<'a>(ptr: *mut bool) -> &'a AtomicBool {
|
@@ -1151,18 +1202,12 @@ impl<T> AtomicPtr<T> {
|
1151 | 1202 | /// * `ptr` must be aligned to `align_of::<AtomicPtr<T>>()` (note that on some platforms this
|
1152 | 1203 | /// can be bigger than `align_of::<*mut T>()`).
|
1153 | 1204 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
1154 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before relationship |
1155 |
| - /// with atomic accesses via the returned value (or vice-versa). |
1156 |
| - /// * In other words, time periods where the value is accessed atomically may not overlap |
1157 |
| - /// with periods where the value is accessed non-atomically. |
1158 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically for the |
1159 |
| - /// duration of lifetime `'a`. Most use cases should be able to follow this guideline. |
1160 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are done |
1161 |
| - /// from the same thread. |
1162 |
| - /// * This method should not be used to create overlapping or mixed-size atomic accesses, as |
1163 |
| - /// these are not supported by the memory model. |
| 1205 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 1206 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 1207 | + /// without synchronization. |
1164 | 1208 | ///
|
1165 | 1209 | /// [valid]: crate::ptr#safety
|
| 1210 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
1166 | 1211 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
1167 | 1212 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
1168 | 1213 | pub const unsafe fn from_ptr<'a>(ptr: *mut *mut T) -> &'a AtomicPtr<T> {
|
@@ -2133,19 +2178,12 @@ macro_rules! atomic_int {
|
2133 | 2178 | `align_of::<", stringify!($atomic_type), ">()` (note that on some platforms this \
|
2134 | 2179 | can be bigger than `align_of::<", stringify!($int_type), ">()`).")]
|
2135 | 2180 | /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
|
2136 |
| - /// * Non-atomic accesses to the value behind `ptr` must have a happens-before |
2137 |
| - /// relationship with atomic accesses via the returned value (or vice-versa). |
2138 |
| - /// * In other words, time periods where the value is accessed atomically may not |
2139 |
| - /// overlap with periods where the value is accessed non-atomically. |
2140 |
| - /// * This requirement is trivially satisfied if `ptr` is never used non-atomically |
2141 |
| - /// for the duration of lifetime `'a`. Most use cases should be able to follow |
2142 |
| - /// this guideline. |
2143 |
| - /// * This requirement is also trivially satisfied if all accesses (atomic or not) are |
2144 |
| - /// done from the same thread. |
2145 |
| - /// * This method should not be used to create overlapping or mixed-size atomic |
2146 |
| - /// accesses, as these are not supported by the memory model. |
| 2181 | + /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not |
| 2182 | + /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, |
| 2183 | + /// without synchronization. |
2147 | 2184 | ///
|
2148 | 2185 | /// [valid]: crate::ptr#safety
|
| 2186 | + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses |
2149 | 2187 | #[stable(feature = "atomic_from_ptr", since = "CURRENT_RUSTC_VERSION")]
|
2150 | 2188 | #[rustc_const_unstable(feature = "const_atomic_from_ptr", issue = "108652")]
|
2151 | 2189 | pub const unsafe fn from_ptr<'a>(ptr: *mut $int_type) -> &'a $atomic_type {
|
|
0 commit comments