|
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