Skip to main content

atomic_maybe_uninit/
raw.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3//! Low level API.
4
5#[cfg(doc)]
6use core::{
7    cell::UnsafeCell,
8    sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst},
9};
10use core::{mem::MaybeUninit, sync::atomic::Ordering};
11
12// TODO(semver): merge AtomicLoad and AtomicStore and rename to AtomicLoadStore?
13
14/// Primitive types that may support atomic operations.
15///
16/// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`.
17///
18/// Currently this is implemented only for integer types.
19pub trait Primitive: crate::private::PrimitivePriv {}
20
21/// Atomic load.
22///
23/// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`.
24#[cfg_attr(
25    not(atomic_maybe_uninit_no_diagnostic_namespace),
26    diagnostic::on_unimplemented(
27        message = "atomic load of `{Self}` is not available on this target",
28        label = "this associated function is not available on this target",
29        note = "see <https://docs.rs/atomic-maybe-uninit/latest/atomic_maybe_uninit/#platform-support> for more."
30    )
31)]
32pub trait AtomicLoad: Primitive {
33    /// Loads a value from `src`.
34    ///
35    /// `atomic_load` takes an [`Ordering`] argument which describes the memory ordering of this operation.
36    /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`].
37    ///
38    /// # Safety
39    ///
40    /// Behavior is undefined if any of the following conditions are violated:
41    ///
42    /// - `src` must be [valid] for reads.
43    /// - `src` must be aligned to `size_of::<MaybeUninit<T>>()` (note that on some platforms this
44    ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
45    /// - `order` must be [`SeqCst`], [`Acquire`], or [`Relaxed`].
46    /// - You must adhere to the [Memory model for atomic accesses]. In particular, it is not
47    ///   allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different
48    ///   sizes, without synchronization.
49    ///
50    /// Compatibility with read-only memory applies only to relaxed operations with a register or smaller width.
51    /// See the ["Atomic accesses to read-only memory" section in the `core::sync::atomic` docs][read-only-memory]
52    /// for more.
53    ///
54    /// [valid]: core::ptr#safety
55    /// [Memory model for atomic accesses]: core::sync::atomic#memory-model-for-atomic-accesses
56    /// [read-only-memory]: core::sync::atomic#atomic-accesses-to-read-only-memory
57    unsafe fn atomic_load(src: *const MaybeUninit<Self>, order: Ordering) -> MaybeUninit<Self>;
58}
59
60/// Atomic store.
61///
62/// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`.
63#[cfg_attr(
64    not(atomic_maybe_uninit_no_diagnostic_namespace),
65    diagnostic::on_unimplemented(
66        message = "atomic store of `{Self}` is not available on this target",
67        label = "this associated function is not available on this target",
68        note = "see <https://docs.rs/atomic-maybe-uninit/latest/atomic_maybe_uninit/#platform-support> for more."
69    )
70)]
71pub trait AtomicStore: Primitive {
72    /// Stores a value into `dst`.
73    ///
74    /// `atomic_store` takes an [`Ordering`] argument which describes the memory ordering of this operation.
75    ///  Possible values are [`SeqCst`], [`Release`] and [`Relaxed`].
76    ///
77    /// # Safety
78    ///
79    /// Behavior is undefined if any of the following conditions are violated:
80    ///
81    /// - `dst` must be [valid] for writes
82    /// - `dst` must be aligned to `size_of::<MaybeUninit<T>>()` (note that on some platforms this
83    ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
84    /// - `order` must be [`SeqCst`], [`Release`], or [`Relaxed`].
85    /// - You must adhere to the [Memory model for atomic accesses]. In particular, it is not
86    ///   allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different
87    ///   sizes, without synchronization.
88    ///
89    /// Compatibility with write-only memory applies only to relaxed operations with a register or smaller width.
90    /// See the ["Atomic accesses to read-only memory" section in the `core::sync::atomic` docs][read-only-memory]
91    /// for more.
92    ///
93    /// [valid]: core::ptr#safety
94    /// [Memory model for atomic accesses]: core::sync::atomic#memory-model-for-atomic-accesses
95    /// [read-only-memory]: core::sync::atomic#atomic-accesses-to-read-only-memory
96    #[inline]
97    unsafe fn atomic_store(dst: *mut MaybeUninit<Self>, val: MaybeUninit<Self>, order: Ordering) {
98        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
99        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
100        let val = core::hint::black_box(val);
101        // SAFETY: the caller must uphold the safety contract.
102        unsafe { Self::__atomic_store_impl(dst, val, order) }
103    }
104
105    #[doc(hidden)] // Not public API.
106    unsafe fn __atomic_store_impl(
107        dst: *mut MaybeUninit<Self>,
108        val: MaybeUninit<Self>,
109        order: Ordering,
110    );
111}
112
113/// Atomic swap.
114///
115/// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`.
116#[cfg_attr(
117    not(atomic_maybe_uninit_no_diagnostic_namespace),
118    diagnostic::on_unimplemented(
119        message = "atomic swap of `{Self}` is not available on this target",
120        label = "this associated function is not available on this target",
121        note = "see <https://docs.rs/atomic-maybe-uninit/latest/atomic_maybe_uninit/#platform-support> for more."
122    )
123)]
124pub trait AtomicSwap: AtomicLoad + AtomicStore {
125    /// Stores a value into `dst`, returning the previous value.
126    ///
127    /// `atomic_swap` takes an [`Ordering`] argument which describes the memory ordering
128    /// of this operation. All ordering modes are possible. Note that using
129    /// [`Acquire`] makes the store part of this operation [`Relaxed`], and
130    /// using [`Release`] makes the load part [`Relaxed`].
131    ///
132    /// # Safety
133    ///
134    /// Behavior is undefined if any of the following conditions are violated:
135    ///
136    /// - `dst` must be [valid] for both reads and writes.
137    /// - `dst` must be aligned to `size_of::<MaybeUninit<T>>()` (note that on some platforms this
138    ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
139    /// - `order` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`].
140    /// - You must adhere to the [Memory model for atomic accesses]. In particular, it is not
141    ///   allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different
142    ///   sizes, without synchronization.
143    ///
144    /// [valid]: core::ptr#safety
145    /// [Memory model for atomic accesses]: core::sync::atomic#memory-model-for-atomic-accesses
146    #[inline]
147    unsafe fn atomic_swap(
148        dst: *mut MaybeUninit<Self>,
149        val: MaybeUninit<Self>,
150        order: Ordering,
151    ) -> MaybeUninit<Self> {
152        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
153        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
154        let val = core::hint::black_box(val);
155        // SAFETY: the caller must uphold the safety contract.
156        unsafe { Self::__atomic_swap_impl(dst, val, order) }
157    }
158
159    #[doc(hidden)] // Not public API.
160    unsafe fn __atomic_swap_impl(
161        dst: *mut MaybeUninit<Self>,
162        val: MaybeUninit<Self>,
163        order: Ordering,
164    ) -> MaybeUninit<Self>;
165}
166
167/// Atomic compare and exchange.
168///
169/// This trait is sealed and cannot be implemented for types outside of `atomic-maybe-uninit`.
170#[cfg_attr(
171    not(atomic_maybe_uninit_no_diagnostic_namespace),
172    diagnostic::on_unimplemented(
173        message = "atomic compare and exchange of `{Self}` is not available on this target",
174        label = "this associated function is not available on this target",
175        note = "see <https://docs.rs/atomic-maybe-uninit/latest/atomic_maybe_uninit/#platform-support> for more."
176    )
177)]
178pub trait AtomicCompareExchange: AtomicLoad + AtomicStore {
179    /// Stores a value into `dst` if the current value is the same as
180    /// the `current` value. Here, "the same" is determined using byte-wise
181    /// equality, not `PartialEq`.
182    ///
183    /// The return value is a tuple of the previous value and the result indicating whether the new
184    /// value was written and containing the previous value. On success, the returned value is
185    /// guaranteed to be equal to the value at `current`.
186    ///
187    /// `atomic_compare_exchange` takes two [`Ordering`] arguments to describe the memory
188    /// ordering of this operation. `success` describes the required ordering for the
189    /// read-modify-write operation that takes place if the comparison with `current` succeeds.
190    /// `failure` describes the required ordering for the load operation that takes place when
191    /// the comparison fails. Using [`Acquire`] as success ordering makes the store part
192    /// of this operation [`Relaxed`], and using [`Release`] makes the successful load
193    /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
194    ///
195    /// # Safety
196    ///
197    /// Behavior is undefined if any of the following conditions are violated:
198    ///
199    /// - `dst` must be valid for both reads and writes.
200    /// - `dst` must be aligned to `size_of::<MaybeUninit<T>>()` (note that on some platforms this
201    ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
202    /// - `success` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`].
203    /// - `failure` must be [`SeqCst`], [`Acquire`], or [`Relaxed`].
204    /// - You must adhere to the [Memory model for atomic accesses]. In particular, it is not
205    ///   allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different
206    ///   sizes, without synchronization.
207    ///
208    /// [valid]: core::ptr#safety
209    /// [Memory model for atomic accesses]: core::sync::atomic#memory-model-for-atomic-accesses
210    ///
211    /// # Notes
212    ///
213    /// Comparison of two values containing uninitialized bytes may fail even if
214    /// they are equivalent as Rust's type, because values can be byte-wise
215    /// inequal even when they are equal as Rust values.
216    ///
217    /// See [`AtomicMaybeUninit::compare_exchange`](crate::AtomicMaybeUninit::compare_exchange) for details.
218    #[inline]
219    unsafe fn atomic_compare_exchange(
220        dst: *mut MaybeUninit<Self>,
221        current: MaybeUninit<Self>,
222        new: MaybeUninit<Self>,
223        success: Ordering,
224        failure: Ordering,
225    ) -> (MaybeUninit<Self>, bool) {
226        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
227        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
228        let current = core::hint::black_box(current);
229        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
230        let new = core::hint::black_box(new);
231        // SAFETY: the caller must uphold the safety contract.
232        unsafe { Self::__atomic_compare_exchange_impl(dst, current, new, success, failure) }
233    }
234
235    #[doc(hidden)] // Not public API.
236    unsafe fn __atomic_compare_exchange_impl(
237        dst: *mut MaybeUninit<Self>,
238        current: MaybeUninit<Self>,
239        new: MaybeUninit<Self>,
240        success: Ordering,
241        failure: Ordering,
242    ) -> (MaybeUninit<Self>, bool);
243
244    /// Stores a value into `dst` if the current value is the same as
245    /// the `current` value. Here, "the same" is determined using byte-wise
246    /// equality, not `PartialEq`.
247    ///
248    /// This function is allowed to spuriously fail even when the comparison succeeds, which can
249    /// result in more efficient code on some platforms. The return value is a tuple of the previous
250    /// value and the result indicating whether the new value was written and containing the
251    /// previous value.
252    ///
253    /// `atomic_compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory
254    /// ordering of this operation. `success` describes the required ordering for the
255    /// read-modify-write operation that takes place if the comparison with `current` succeeds.
256    /// `failure` describes the required ordering for the load operation that takes place when
257    /// the comparison fails. Using [`Acquire`] as success ordering makes the store part
258    /// of this operation [`Relaxed`], and using [`Release`] makes the successful load
259    /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
260    ///
261    /// # Safety
262    ///
263    /// Behavior is undefined if any of the following conditions are violated:
264    ///
265    /// - `dst` must be [valid] for both reads and writes.
266    /// - `dst` must be aligned to `size_of::<MaybeUninit<T>>()` (note that on some platforms this
267    ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
268    /// - `success` must be [`SeqCst`], [`AcqRel`], [`Acquire`], [`Release`], or [`Relaxed`].
269    /// - `failure` must be [`SeqCst`], [`Acquire`], or [`Relaxed`].
270    /// - You must adhere to the [Memory model for atomic accesses]. In particular, it is not
271    ///   allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different
272    ///   sizes, without synchronization.
273    ///
274    /// [valid]: core::ptr#safety
275    /// [Memory model for atomic accesses]: core::sync::atomic#memory-model-for-atomic-accesses
276    ///
277    /// # Notes
278    ///
279    /// Comparison of two values containing uninitialized bytes may fail even if
280    /// they are equivalent as Rust's type, because values can be byte-wise
281    /// inequal even when they are equal as Rust values.
282    ///
283    /// See [`AtomicMaybeUninit::compare_exchange`](crate::AtomicMaybeUninit::compare_exchange) for details.
284    #[inline]
285    unsafe fn atomic_compare_exchange_weak(
286        dst: *mut MaybeUninit<Self>,
287        current: MaybeUninit<Self>,
288        new: MaybeUninit<Self>,
289        success: Ordering,
290        failure: Ordering,
291    ) -> (MaybeUninit<Self>, bool) {
292        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
293        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
294        let current = core::hint::black_box(current);
295        #[cfg(not(atomic_maybe_uninit_llvm_20_or_later))]
296        let new = core::hint::black_box(new);
297        // SAFETY: the caller must uphold the safety contract.
298        unsafe { Self::__atomic_compare_exchange_weak_impl(dst, current, new, success, failure) }
299    }
300
301    #[doc(hidden)] // Not public API.
302    #[inline]
303    unsafe fn __atomic_compare_exchange_weak_impl(
304        dst: *mut MaybeUninit<Self>,
305        current: MaybeUninit<Self>,
306        new: MaybeUninit<Self>,
307        success: Ordering,
308        failure: Ordering,
309    ) -> (MaybeUninit<Self>, bool) {
310        // SAFETY: the caller must uphold the safety contract.
311        unsafe { Self::__atomic_compare_exchange_impl(dst, current, new, success, failure) }
312    }
313}