Skip to main content

atomic_maybe_uninit/
lib.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3/*!
4<!-- Note: Document from sync-markdown-to-rustdoc:start through sync-markdown-to-rustdoc:end
5     is synchronized from README.md. Any changes to that range are not preserved. -->
6<!-- tidy:sync-markdown-to-rustdoc:start -->
7
8Atomic operations on potentially uninitialized integers.
9
10## Motivation
11
12Copying types containing uninitialized bytes (e.g., padding), via the standard library's atomic types
13is [undefined behavior because the copy goes through integers][undefined-behavior].
14
15This crate provides a way to soundly perform such operations.
16
17## Platform Support
18
19Currently, all CPU architectures supported by Rust (x86, x86_64, Arm, AArch64, Arm64EC, RISC-V, LoongArch, s390x, PowerPC, MIPS, SPARC, AVR, MSP430, Hexagon, M68k, C-SKY, and Xtensa) are supported.
20(You can use `cfg_{has,no}_*` macros to write code based on which primitive sizes are available for the current target and Rust version.)
21
22| target_arch                                 | primitives                                          | load/store | swap/CAS |
23| ------------------------------------------- | --------------------------------------------------- |:----------:|:--------:|
24| x86                                         | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
25| x86_64                                      | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
26| x86_64 (+cmpxchg16b) \[2]                   | i128,u128                                           | ✓          | ✓        |
27| arm (v6+ or Linux/Android)                  | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
28| arm (except for M-profile) \[3]             | i64,u64                                             | ✓          | ✓        |
29| aarch64                                     | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓          | ✓        |
30| arm64ec \[10]                               | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓          | ✓        |
31| riscv32                                     | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
32| riscv32 (+zacas) \[4]                       | i64,u64                                             | ✓          | ✓        |
33| riscv64                                     | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓\[1]    |
34| riscv64 (+zacas) \[4]                       | i128,u128                                           | ✓          | ✓        |
35| loongarch64                                 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
36| loongarch32 \[11]                           | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓        |
37| s390x \[10]                                 | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64,i128,u128 | ✓          | ✓        |
38| powerpc \[12]                               | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓        |
39| powerpc64 \[12]                             | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
40| powerpc64 (+quadword-atomics) \[6] \[12]    | i128,u128                                           | ✓          | ✓        |
41| mips / mips32r6 \[13]                       | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓        |
42| mips64 / mips64r6 \[13]                     | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
43| sparc \[13] (experimental)                  | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
44| sparc (+v8plus) \[8] \[13] (experimental)   | i64,u64                                             | ✓          | ✓        |
45| sparc64 \[13] (experimental)                | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
46| avr \[13] (experimental)                    | isize,usize,i8,u8,i16,u16                           | ✓          | ✓        |
47| msp430 \[13] (experimental)                 | isize,usize,i8,u8,i16,u16                           | ✓          | ✓        |
48| hexagon \[13] (experimental)                | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64           | ✓          | ✓        |
49| m68k \[13] (experimental)                   | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
50| m68k (+isa-68020) \[9] \[13] (experimental) | i64,u64                                             | ✓          | ✓        |
51| csky \[13] (experimental)                   | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
52| xtensa \[13] (experimental)                 | isize,usize,i8,u8,i16,u16,i32,u32                   | ✓          | ✓\[1]    |
53
54\[1] Arm's RMW operations are not available on Armv6-M (thumbv6m). RISC-V's RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. 32-bit SPARC's RMW operations requires `v9` or `leoncasa` target feature (enabled by default on Linux). M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* or c860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.<br>
55\[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).<br>
56\[3] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.<br>
57\[4] Requires `zacas` target feature.<br>
58\[6] Requires `quadword-atomics` target feature (enabled by default on powerpc64le).<br>
59\[8] Requires `v9` and `v8plus` target features (both enabled by default on Linux).<br>
60\[9] Requires target-cpu M68020+ (enabled by default on Linux).<br>
61\[10] Requires Rust 1.84+.<br>
62\[11] Requires Rust 1.91+.<br>
63\[12] Requires Rust 1.95+.<br>
64\[13] Requires nightly due to `#![feature(asm_experimental_arch)]`.<br>
65<!-- loongarch64: \[5] Requires `scq` target feature.<br> -->
66<!-- mips32r6/mips64r6: \[7] Requires Release 6 Paired LL/SC family of instructions.<br> -->
67
68See also [Atomic operation overview by architecture](https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/README.md)
69for more information about atomic operations in these architectures.
70
71Feel free to submit an issue if your target is not supported yet.
72
73## Limitations
74
75This crate uses inline assembly to implement atomic operations (this is currently the only sound way to perform atomic operations on uninitialized values), so it is not compatible with [Miri](https://github.com/rust-lang/miri/issues/11) and [most kinds of Sanitizers](https://github.com/google/sanitizers/issues/192).
76
77## Related Projects
78
79- [portable-atomic]: Portable atomic types including support for 128-bit atomics, atomic float, etc.
80- [atomic-memcpy]: Byte-wise atomic memcpy.
81- [asmtest]: A library for tracking generated assemblies.
82
83[asmtest]: https://github.com/taiki-e/asmtest
84[atomic-memcpy]: https://github.com/taiki-e/atomic-memcpy
85[portable-atomic]: https://github.com/taiki-e/portable-atomic
86[undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
87
88<!-- tidy:sync-markdown-to-rustdoc:end -->
89*/
90
91#![no_std]
92#![doc(test(
93    no_crate_inject,
94    attr(allow(
95        dead_code,
96        unused_variables,
97        clippy::undocumented_unsafe_blocks,
98        clippy::unused_trait_names,
99    ))
100))]
101#![warn(
102    // Lints that may help when writing public library.
103    missing_debug_implementations,
104    missing_docs,
105    clippy::alloc_instead_of_core,
106    clippy::exhaustive_enums,
107    clippy::exhaustive_structs,
108    clippy::impl_trait_in_params,
109    clippy::std_instead_of_alloc,
110    clippy::std_instead_of_core,
111    clippy::missing_inline_in_public_items,
112    // Code outside of cfg(test) shouldn't use float.
113    clippy::float_arithmetic,
114    // Code outside of cfg(test) shouldn't use code that can panic except for assertions. (overflow also cause panic if overflow check is enabled)
115    clippy::arithmetic_side_effects,
116)]
117#![cfg_attr(atomic_maybe_uninit_no_strict_provenance, allow(unstable_name_collisions))]
118#![allow(clippy::inline_always, clippy::unreadable_literal, clippy::used_underscore_items)]
119#![cfg_attr(
120    all(
121        atomic_maybe_uninit_unstable_asm_experimental_arch,
122        not(any(
123            // These cases currently don't use asm!
124            all(target_arch = "sparc", atomic_maybe_uninit_no_stbar),
125            all(target_arch = "mips", atomic_maybe_uninit_no_sync),
126        )),
127    ),
128    feature(asm_experimental_arch)
129)]
130
131// There are currently no 128-bit or higher builtin targets.
132// (Although some of our generic code is written with the future
133// addition of 128-bit targets in mind.)
134// Note that Rust (and C99) pointers must be at least 16-bit (i.e., 8-bit targets are impossible): https://github.com/rust-lang/rust/pull/49305
135#[cfg(not(any(
136    target_pointer_width = "16",
137    target_pointer_width = "32",
138    target_pointer_width = "64",
139)))]
140compile_error!(
141    "atomic-maybe-uninit currently only supports targets with {16,32,64}-bit pointer width; \
142     if you need support for others, \
143     please submit an issue at <https://github.com/taiki-e/atomic-maybe-uninit>"
144);
145
146#[cfg(test)]
147extern crate std;
148
149#[macro_use]
150mod utils;
151
152#[cfg(test)]
153#[macro_use]
154mod tests;
155
156pub mod raw;
157
158#[cfg(doc)]
159use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
160use core::{
161    cell::UnsafeCell,
162    fmt,
163    mem::{self, MaybeUninit},
164    sync::atomic::Ordering,
165};
166
167use self::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap, Primitive};
168
169// -----------------------------------------------------------------------------
170// AtomicMaybeUninit
171
172/// A potentially uninitialized integer type which can be safely shared between threads.
173///
174/// This type has the same in-memory representation as the underlying
175/// value type, `MaybeUninit<T>`.
176#[repr(C)]
177pub struct AtomicMaybeUninit<T: Primitive> {
178    v: UnsafeCell<MaybeUninit<T>>,
179    /// `[T::Align; 0]` ensures alignment is at least that of `T::Align`.
180    ///
181    /// This is needed because x86's u64 is 4-byte aligned and x86_64's u128 is
182    /// 8-byte aligned and atomic operations normally require alignment greater
183    /// than or equal to the size.
184    _align: [T::Align; 0],
185}
186
187impl<T: Primitive> From<MaybeUninit<T>> for AtomicMaybeUninit<T> {
188    /// Creates a new atomic value from a potentially uninitialized value.
189    #[inline]
190    fn from(v: MaybeUninit<T>) -> Self {
191        Self::new(v)
192    }
193}
194
195impl<T: Primitive> From<T> for AtomicMaybeUninit<T> {
196    /// Creates a new atomic value from an initialized value.
197    #[inline]
198    fn from(v: T) -> Self {
199        Self::new(MaybeUninit::new(v))
200    }
201}
202
203impl<T: Primitive> fmt::Debug for AtomicMaybeUninit<T> {
204    #[inline] // fmt is not hot path, but #[inline] on fmt seems to still be useful: https://github.com/rust-lang/rust/pull/117727
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        f.write_str(core::any::type_name::<Self>())
207    }
208}
209
210// Send is implicitly implemented.
211// SAFETY: `T` is `Send` and any data races are prevented by atomic intrinsics.
212unsafe impl<T: Primitive> Sync for AtomicMaybeUninit<T> {}
213
214// UnwindSafe is implicitly implemented.
215impl<T: Primitive> core::panic::RefUnwindSafe for AtomicMaybeUninit<T> {}
216
217impl<T: Primitive> AtomicMaybeUninit<T> {
218    /// Creates a new atomic value from a potentially uninitialized value.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// use std::mem::MaybeUninit;
224    ///
225    /// use atomic_maybe_uninit::AtomicMaybeUninit;
226    ///
227    /// let v = AtomicMaybeUninit::new(MaybeUninit::new(5_i32));
228    ///
229    /// // Equivalent to:
230    /// let v = AtomicMaybeUninit::from(5_i32);
231    /// ```
232    #[inline]
233    #[must_use]
234    pub const fn new(v: MaybeUninit<T>) -> Self {
235        Self { v: UnsafeCell::new(v), _align: [] }
236    }
237
238    // TODO: update docs based on https://github.com/rust-lang/rust/pull/116762
239    const_fn! {
240        const_if: #[cfg(not(atomic_maybe_uninit_no_const_mut_refs))];
241        /// Creates a new reference to an atomic value from a pointer.
242        ///
243        /// This is `const fn` on Rust 1.83+.
244        ///
245        /// # Safety
246        ///
247        /// * `ptr` must be aligned to `align_of::<AtomicMaybeUninit<T>>()` (note that on some platforms this
248        ///   can be bigger than `align_of::<MaybeUninit<T>>()`).
249        /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`.
250        /// * Non-atomic accesses to the value behind `ptr` must have a happens-before
251        ///   relationship with atomic accesses via the returned value (or vice-versa).
252        ///   * In other words, time periods where the value is accessed atomically may not
253        ///     overlap with periods where the value is accessed non-atomically.
254        ///   * This requirement is trivially satisfied if `ptr` is never used non-atomically
255        ///     for the duration of lifetime `'a`. Most use cases should be able to follow
256        ///     this guideline.
257        ///   * This requirement is also trivially satisfied if all accesses (atomic or not) are
258        ///     done from the same thread.
259        /// * This method must not be used to create overlapping or mixed-size atomic
260        ///   accesses, as these are not supported by the memory model.
261        ///
262        /// [valid]: core::ptr#safety
263        #[inline]
264        #[must_use]
265        pub const unsafe fn from_ptr<'a>(ptr: *mut MaybeUninit<T>) -> &'a Self {
266            // SAFETY: guaranteed by the caller
267            unsafe { &*ptr.cast::<Self>() }
268        }
269    }
270
271    const_fn! {
272        const_if: #[cfg(not(atomic_maybe_uninit_no_const_mut_refs))];
273        /// Returns a mutable reference to the underlying value.
274        ///
275        /// This is safe because the mutable reference guarantees that no other threads are
276        /// concurrently accessing the atomic data.
277        ///
278        /// This is `const fn` on Rust 1.83+.
279        ///
280        /// # Examples
281        ///
282        /// ```
283        /// use std::mem::MaybeUninit;
284        ///
285        /// use atomic_maybe_uninit::AtomicMaybeUninit;
286        ///
287        /// let mut v = AtomicMaybeUninit::from(5_i32);
288        /// unsafe { assert_eq!((*v.get_mut()).assume_init(), 5) }
289        /// *v.get_mut() = MaybeUninit::new(10);
290        /// unsafe { assert_eq!((*v.get_mut()).assume_init(), 10) }
291        /// ```
292        #[inline]
293        pub const fn get_mut(&mut self) -> &mut MaybeUninit<T> {
294            // SAFETY: the mutable reference guarantees unique ownership.
295            // (core::cell::UnsafeCell::get_mut requires newer nightly)
296            unsafe { &mut *self.as_ptr() }
297        }
298    }
299
300    /// Consumes the atomic and returns the contained value.
301    ///
302    /// This is safe because passing `self` by value guarantees that no other threads are
303    /// concurrently accessing the atomic data.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use atomic_maybe_uninit::AtomicMaybeUninit;
309    ///
310    /// let v = AtomicMaybeUninit::from(5_i32);
311    /// unsafe { assert_eq!(v.into_inner().assume_init(), 5) }
312    /// ```
313    #[inline]
314    pub const fn into_inner(self) -> MaybeUninit<T> {
315        // SAFETY: AtomicMaybeUninit<T> and MaybeUninit<T> have the same size
316        // and in-memory representations, so they can be safely transmuted.
317        // (Equivalent to UnsafeCell::into_inner which is unstable in const context.)
318        unsafe { utils::transmute_copy_by_val::<Self, MaybeUninit<T>>(self) }
319    }
320
321    /// Loads a value from the atomic value.
322    ///
323    /// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation.
324    /// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`].
325    ///
326    /// # Panics
327    ///
328    /// Panics if `order` is [`Release`] or [`AcqRel`].
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use std::sync::atomic::Ordering;
334    ///
335    /// use atomic_maybe_uninit::AtomicMaybeUninit;
336    ///
337    /// let v = AtomicMaybeUninit::from(5_i32);
338    /// unsafe { assert_eq!(v.load(Ordering::Relaxed).assume_init(), 5) }
339    /// ```
340    #[inline]
341    #[cfg_attr(debug_assertions, track_caller)]
342    pub fn load(&self, order: Ordering) -> MaybeUninit<T>
343    where
344        T: AtomicLoad,
345    {
346        utils::assert_load_ordering(order);
347        // SAFETY: any data races are prevented by atomic intrinsics, the raw
348        // pointer passed in is valid because we got it from a reference,
349        // and we've checked the order is valid. Alignment is upheld because
350        // `PrimitivePriv`'s safety requirement ensures sufficient alignment
351        // of `T::Align`, and we got our `_align` field.
352        unsafe { T::atomic_load(self.v.get(), order) }
353    }
354
355    /// Stores a value into the atomic value.
356    ///
357    /// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation.
358    ///  Possible values are [`SeqCst`], [`Release`] and [`Relaxed`].
359    ///
360    /// # Panics
361    ///
362    /// Panics if `order` is [`Acquire`] or [`AcqRel`].
363    ///
364    /// # Examples
365    ///
366    /// ```
367    /// use std::{mem::MaybeUninit, sync::atomic::Ordering};
368    ///
369    /// use atomic_maybe_uninit::AtomicMaybeUninit;
370    ///
371    /// let v = AtomicMaybeUninit::from(5_i32);
372    /// v.store(MaybeUninit::new(10), Ordering::Relaxed);
373    /// unsafe { assert_eq!(v.load(Ordering::Relaxed).assume_init(), 10) }
374    /// ```
375    #[inline]
376    #[cfg_attr(debug_assertions, track_caller)]
377    pub fn store(&self, val: MaybeUninit<T>, order: Ordering)
378    where
379        T: AtomicStore,
380    {
381        utils::assert_store_ordering(order);
382        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
383        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
384        let val = core::hint::black_box(val);
385        // SAFETY: any data races are prevented by atomic intrinsics, the raw
386        // pointer passed in is valid because we got it from a reference,
387        // and we've checked the order is valid. Alignment is upheld because
388        // `PrimitivePriv`'s safety requirement ensures sufficient alignment
389        // of `T::Align`, and we got our `_align` field.
390        unsafe { T::atomic_store(self.v.get(), val, order) }
391    }
392
393    /// Stores a value into the atomic value, returning the previous value.
394    ///
395    /// `swap` takes an [`Ordering`] argument which describes the memory ordering
396    /// of this operation. All ordering modes are possible. Note that using
397    /// [`Acquire`] makes the store part of this operation [`Relaxed`], and
398    /// using [`Release`] makes the load part [`Relaxed`].
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// use std::{mem::MaybeUninit, sync::atomic::Ordering};
404    ///
405    /// use atomic_maybe_uninit::AtomicMaybeUninit;
406    ///
407    /// let v = AtomicMaybeUninit::from(5_i32);
408    /// unsafe {
409    ///     assert_eq!(v.swap(MaybeUninit::new(10), Ordering::Relaxed).assume_init(), 5);
410    ///     assert_eq!(v.load(Ordering::Relaxed).assume_init(), 10);
411    /// }
412    /// ```
413    #[inline]
414    pub fn swap(&self, val: MaybeUninit<T>, order: Ordering) -> MaybeUninit<T>
415    where
416        T: AtomicSwap,
417    {
418        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
419        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
420        let val = core::hint::black_box(val);
421        // SAFETY: any data races are prevented by atomic intrinsics and the raw
422        // pointer passed in is valid because we got it from a reference.
423        // Alignment is upheld because `PrimitivePriv`'s safety requirement
424        // ensures sufficient alignment of `T::Align`, and we got our `_align`
425        // field.
426        unsafe { T::atomic_swap(self.v.get(), val, order) }
427    }
428
429    /// Stores a value into the atomic value if the current value is the same as
430    /// the `current` value. Here, "the same" is determined using byte-wise
431    /// equality, not `PartialEq`.
432    ///
433    /// The return value is a result indicating whether the new value was written and
434    /// containing the previous value. On success this value is guaranteed to be equal to
435    /// `current`.
436    ///
437    /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory
438    /// ordering of this operation. `success` describes the required ordering for the
439    /// read-modify-write operation that takes place if the comparison with `current` succeeds.
440    /// `failure` describes the required ordering for the load operation that takes place when
441    /// the comparison fails. Using [`Acquire`] as success ordering makes the store part
442    /// of this operation [`Relaxed`], and using [`Release`] makes the successful load
443    /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
444    ///
445    /// # Panics
446    ///
447    /// Panics if `failure` is [`Release`], [`AcqRel`].
448    ///
449    /// # Notes
450    ///
451    /// Comparison of two values containing uninitialized bytes may fail even if
452    /// they are equivalent as Rust's type, because values can be byte-wise
453    /// inequal even when they are equal as Rust values.
454    ///
455    /// For example, the following example could be an infinite loop:
456    ///
457    /// ```no_run
458    /// use std::{
459    ///     mem::{self, MaybeUninit},
460    ///     sync::atomic::Ordering,
461    /// };
462    ///
463    /// use atomic_maybe_uninit::AtomicMaybeUninit;
464    ///
465    /// #[derive(Clone, Copy, PartialEq, Eq)]
466    /// #[repr(C, align(4))]
467    /// struct Test(u8, u16);
468    ///
469    /// unsafe {
470    ///     let x = mem::transmute::<Test, MaybeUninit<u32>>(Test(0, 0));
471    ///     let v = AtomicMaybeUninit::new(x);
472    ///     while v
473    ///         .compare_exchange(
474    ///             mem::transmute::<Test, MaybeUninit<u32>>(Test(0, 0)),
475    ///             mem::transmute::<Test, MaybeUninit<u32>>(Test(1, 0)),
476    ///             Ordering::AcqRel,
477    ///             Ordering::Acquire,
478    ///         )
479    ///         .is_err()
480    ///     {}
481    /// }
482    /// ```
483    ///
484    /// To work around this problem, you need to use a helper like the following.
485    ///
486    /// ```
487    /// # use std::{
488    /// #     mem::{self, MaybeUninit},
489    /// #     sync::atomic::Ordering,
490    /// # };
491    /// # use atomic_maybe_uninit::AtomicMaybeUninit;
492    /// # #[derive(Clone, Copy, PartialEq, Eq)]
493    /// # #[repr(C, align(4))]
494    /// # struct Test(u8, u16);
495    /// // Adapted from https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-utils-0.8.10/crossbeam-utils/src/atomic/atomic_cell.rs#L1081-L1110
496    /// unsafe fn atomic_compare_exchange(
497    ///     v: &AtomicMaybeUninit<u32>,
498    ///     mut current: Test,
499    ///     new: Test,
500    /// ) -> Result<Test, Test> {
501    ///     let mut current_raw = unsafe { mem::transmute::<Test, MaybeUninit<u32>>(current) };
502    ///     let new_raw = unsafe { mem::transmute::<Test, MaybeUninit<u32>>(new) };
503    ///     loop {
504    ///         match v.compare_exchange_weak(current_raw, new_raw, Ordering::AcqRel, Ordering::Acquire)
505    ///         {
506    ///             Ok(_) => {
507    ///                 // The values are byte-wise equal; for `Test` we know this implies they are `PartialEq`-equal.
508    ///                 break Ok(current);
509    ///             }
510    ///             Err(previous_raw) => {
511    ///                 let previous = unsafe { mem::transmute::<MaybeUninit<u32>, Test>(previous_raw) };
512    ///
513    ///                 if !Test::eq(&previous, &current) {
514    ///                     break Err(previous);
515    ///                 }
516    ///
517    ///                 // The compare-exchange operation has failed and didn't store `new`. The
518    ///                 // failure is either spurious, or `previous` was semantically equal to
519    ///                 // `current` but not byte-equal. Let's retry with `previous` as the new
520    ///                 // `current`.
521    ///                 current = previous;
522    ///                 current_raw = previous_raw;
523    ///             }
524    ///         }
525    ///     }
526    /// }
527    /// # if cfg!(valgrind) { return; }
528    ///
529    /// unsafe {
530    ///     let x = mem::transmute::<Test, MaybeUninit<u32>>(Test(0, 0));
531    ///     let v = AtomicMaybeUninit::new(x);
532    ///     while atomic_compare_exchange(&v, Test(0, 0), Test(1, 0)).is_err() {}
533    /// }
534    /// ```
535    ///
536    /// Also, Valgrind reports "Conditional jump or move depends on uninitialized value(s)"
537    /// error if there is such a comparison -- which is correct, that's exactly
538    /// what the implementation does, but we are doing this inside inline
539    /// assembly so it should be fine. (Effectively we are adding partial
540    /// `freeze` capabilities to Rust via inline assembly. This pattern has not
541    /// been blessed by the language team, but is also not known to cause any
542    /// problems.)
543    ///
544    /// # Examples
545    ///
546    /// ```
547    /// use std::{mem::MaybeUninit, sync::atomic::Ordering};
548    ///
549    /// use atomic_maybe_uninit::AtomicMaybeUninit;
550    ///
551    /// unsafe {
552    ///     let v = AtomicMaybeUninit::from(5_i32);
553    ///
554    ///     assert_eq!(
555    ///         v.compare_exchange(
556    ///             MaybeUninit::new(5),
557    ///             MaybeUninit::new(10),
558    ///             Ordering::Acquire,
559    ///             Ordering::Relaxed
560    ///         )
561    ///         .unwrap()
562    ///         .assume_init(),
563    ///         5
564    ///     );
565    ///     assert_eq!(v.load(Ordering::Relaxed).assume_init(), 10);
566    ///
567    ///     assert_eq!(
568    ///         v.compare_exchange(
569    ///             MaybeUninit::new(6),
570    ///             MaybeUninit::new(12),
571    ///             Ordering::SeqCst,
572    ///             Ordering::Acquire
573    ///         )
574    ///         .unwrap_err()
575    ///         .assume_init(),
576    ///         10
577    ///     );
578    ///     assert_eq!(v.load(Ordering::Relaxed).assume_init(), 10);
579    /// }
580    /// ```
581    #[doc(alias = "compare_and_swap")]
582    #[inline]
583    #[cfg_attr(debug_assertions, track_caller)]
584    pub fn compare_exchange(
585        &self,
586        current: MaybeUninit<T>,
587        new: MaybeUninit<T>,
588        success: Ordering,
589        failure: Ordering,
590    ) -> Result<MaybeUninit<T>, MaybeUninit<T>>
591    where
592        T: AtomicCompareExchange,
593    {
594        utils::assert_compare_exchange_ordering(success, failure);
595        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
596        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
597        let current = core::hint::black_box(current);
598        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
599        let new = core::hint::black_box(new);
600        // SAFETY: any data races are prevented by atomic intrinsics and the raw
601        // pointer passed in is valid because we got it from a reference.
602        // Alignment is upheld because `PrimitivePriv`'s safety requirement
603        // ensures sufficient alignment of `T::Align`, and we got our `_align`
604        // field.
605        let (out, ok) =
606            unsafe { T::atomic_compare_exchange(self.v.get(), current, new, success, failure) };
607        if ok { Ok(out) } else { Err(out) }
608    }
609
610    /// Stores a value into the atomic value if the current value is the same as
611    /// the `current` value. Here, "the same" is determined using byte-wise
612    /// equality, not `PartialEq`.
613    ///
614    /// This function is allowed to spuriously fail even when the comparison succeeds,
615    /// which can result in more efficient code on some platforms. The return value
616    /// is a result indicating whether the new value was written and containing
617    /// the previous value.
618    ///
619    /// `compare_exchange_weak` takes two [`Ordering`] arguments to describe the memory
620    /// ordering of this operation. `success` describes the required ordering for the
621    /// read-modify-write operation that takes place if the comparison with `current` succeeds.
622    /// `failure` describes the required ordering for the load operation that takes place when
623    /// the comparison fails. Using [`Acquire`] as success ordering makes the store part
624    /// of this operation [`Relaxed`], and using [`Release`] makes the successful load
625    /// [`Relaxed`]. The failure ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
626    ///
627    /// # Panics
628    ///
629    /// Panics if `failure` is [`Release`], [`AcqRel`].
630    ///
631    /// # Notes
632    ///
633    /// Comparison of two values containing uninitialized bytes may fail even if
634    /// they are equivalent as Rust's type, because values can be byte-wise
635    /// inequal even when they are equal as Rust values.
636    ///
637    /// See [`compare_exchange`](Self::compare_exchange) for details.
638    ///
639    /// # Examples
640    ///
641    /// ```
642    /// use std::{mem::MaybeUninit, sync::atomic::Ordering};
643    ///
644    /// use atomic_maybe_uninit::AtomicMaybeUninit;
645    ///
646    /// let v = AtomicMaybeUninit::from(5_i32);
647    ///
648    /// unsafe {
649    ///     let mut old = v.load(Ordering::Relaxed);
650    ///     loop {
651    ///         let new = old.assume_init() * 2;
652    ///         match v.compare_exchange_weak(
653    ///             old,
654    ///             MaybeUninit::new(new),
655    ///             Ordering::SeqCst,
656    ///             Ordering::Relaxed,
657    ///         ) {
658    ///             Ok(_) => break,
659    ///             Err(x) => old = x,
660    ///         }
661    ///     }
662    /// }
663    /// ```
664    #[doc(alias = "compare_and_swap")]
665    #[inline]
666    #[cfg_attr(debug_assertions, track_caller)]
667    pub fn compare_exchange_weak(
668        &self,
669        current: MaybeUninit<T>,
670        new: MaybeUninit<T>,
671        success: Ordering,
672        failure: Ordering,
673    ) -> Result<MaybeUninit<T>, MaybeUninit<T>>
674    where
675        T: AtomicCompareExchange,
676    {
677        utils::assert_compare_exchange_ordering(success, failure);
678        // Workaround LLVM pre-20 bug: https://github.com/rust-lang/rust/issues/129585#issuecomment-2360273081
679        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
680        let current = core::hint::black_box(current);
681        #[cfg(atomic_maybe_uninit_pre_llvm_20)]
682        let new = core::hint::black_box(new);
683        // SAFETY: any data races are prevented by atomic intrinsics and the raw
684        // pointer passed in is valid because we got it from a reference.
685        // Alignment is upheld because `PrimitivePriv`'s safety requirement
686        // ensures sufficient alignment of `T::Align`, and we got our `_align`
687        // field.
688        let (out, ok) = unsafe {
689            T::atomic_compare_exchange_weak(self.v.get(), current, new, success, failure)
690        };
691        if ok { Ok(out) } else { Err(out) }
692    }
693
694    /// Fetches the value, and applies a function to it that returns an optional
695    /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
696    /// `Err(previous_value)`.
697    ///
698    /// Note: This may call the function multiple times if the value has been changed from other threads in
699    /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
700    /// only once to the stored value.
701    ///
702    /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
703    /// The first describes the required ordering for when the operation finally succeeds while the second
704    /// describes the required ordering for loads. These correspond to the success and failure orderings of
705    /// [`compare_exchange`](Self::compare_exchange) respectively.
706    ///
707    /// Using [`Acquire`] as success ordering makes the store part
708    /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load
709    /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`].
710    ///
711    /// # Panics
712    ///
713    /// Panics if `fetch_order` is [`Release`], [`AcqRel`].
714    ///
715    /// # Considerations
716    ///
717    /// This method is not magic; it is not provided by the hardware.
718    /// It is implemented in terms of [`compare_exchange_weak`](Self::compare_exchange_weak),
719    /// and suffers from the same drawbacks.
720    /// In particular, this method will not circumvent the [ABA Problem].
721    ///
722    /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem
723    ///
724    /// # Examples
725    ///
726    /// ```
727    /// use std::{mem::MaybeUninit, sync::atomic::Ordering};
728    ///
729    /// use atomic_maybe_uninit::AtomicMaybeUninit;
730    ///
731    /// unsafe {
732    ///     let v = AtomicMaybeUninit::from(5_i32);
733    ///     assert_eq!(
734    ///         v.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None).unwrap_err().assume_init(),
735    ///         5
736    ///     );
737    ///     assert_eq!(
738    ///         v.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(MaybeUninit::new(
739    ///             x.assume_init() + 1
740    ///         )))
741    ///         .unwrap()
742    ///         .assume_init(),
743    ///         5
744    ///     );
745    ///     assert_eq!(v.load(Ordering::SeqCst).assume_init(), 6);
746    /// }
747    /// ```
748    #[inline]
749    #[cfg_attr(debug_assertions, track_caller)]
750    pub fn fetch_update<F>(
751        &self,
752        set_order: Ordering,
753        fetch_order: Ordering,
754        mut f: F,
755    ) -> Result<MaybeUninit<T>, MaybeUninit<T>>
756    where
757        F: FnMut(MaybeUninit<T>) -> Option<MaybeUninit<T>>,
758        T: AtomicCompareExchange,
759    {
760        let mut prev = self.load(fetch_order);
761        while let Some(next) = f(prev) {
762            match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
763                x @ Ok(_) => return x,
764                Err(next_prev) => prev = next_prev,
765            }
766        }
767        Err(prev)
768    }
769
770    /// Returns a mutable pointer to the underlying value.
771    ///
772    /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
773    /// atomic types work with interior mutability. All modifications of an atomic change the value
774    /// through a shared reference, and can do so safely as long as they use atomic operations. Any
775    /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
776    /// restriction: operations on it must be atomic.
777    #[inline]
778    pub const fn as_ptr(&self) -> *mut MaybeUninit<T> {
779        self.v.get()
780    }
781}
782
783macro_rules! int {
784    ($($ty:ident),* => $align:ident) => {$(
785        impl raw::Primitive for $ty {}
786        const _: () = {
787            assert!(mem::size_of::<AtomicMaybeUninit<$ty>>() == mem::size_of::<$ty>());
788            assert!(mem::align_of::<AtomicMaybeUninit<$ty>>() >= mem::size_of::<$ty>());
789        };
790        // SAFETY: the static assertion above ensures safety requirement.
791        unsafe impl private::PrimitivePriv for $ty {
792            type Align = private::$align;
793        }
794        impl AtomicMaybeUninit<$ty> {
795            /// Creates a new atomic value from a potentially uninitialized value.
796            #[inline]
797            #[must_use]
798            // TODO(semver): remove in the next breaking release.
799            #[deprecated(
800                since = "0.3.10",
801                note = "use `new` instead because it is now always `const fn`"
802            )]
803            pub const fn const_new(v: MaybeUninit<$ty>) -> Self {
804                Self { v: UnsafeCell::new(v), _align: [] }
805            }
806        }
807    )*};
808}
809int!(i8, u8 => Align1);
810int!(i16, u16 => Align2);
811int!(i32, u32 => Align4);
812int!(i64, u64 => Align8);
813int!(i128, u128 => Align16);
814int!(isize, usize => AlignPtr);
815
816#[cfg(target_pointer_width = "16")]
817pub use {cfg_has_atomic_16 as cfg_has_atomic_ptr, cfg_no_atomic_16 as cfg_no_atomic_ptr};
818#[cfg(target_pointer_width = "32")]
819pub use {cfg_has_atomic_32 as cfg_has_atomic_ptr, cfg_no_atomic_32 as cfg_no_atomic_ptr};
820#[cfg(target_pointer_width = "64")]
821pub use {cfg_has_atomic_64 as cfg_has_atomic_ptr, cfg_no_atomic_64 as cfg_no_atomic_ptr};
822#[cfg(target_pointer_width = "128")]
823pub use {cfg_has_atomic_128 as cfg_has_atomic_ptr, cfg_no_atomic_128 as cfg_no_atomic_ptr};
824
825// -----------------------------------------------------------------------------
826// Internals
827
828#[cfg_attr(
829    any(target_arch = "aarch64", all(target_arch = "arm64ec", not(atomic_maybe_uninit_no_asm))),
830    path = "arch/aarch64.rs"
831)]
832#[cfg_attr(
833    all(
834        target_arch = "arm",
835        // Pre-v6 Arm has no Data Memory Barrier (DMB) operation, so we cannot implement non-relaxed atomics.
836        // However, Linux kernel provides helpers for it, so we can provide it on Linux/Android.
837        any(
838            target_feature = "v6",
839            atomic_maybe_uninit_target_feature = "v6",
840            target_os = "linux",
841            target_os = "android",
842        ),
843        // Use armv8.rs for Armv8+.
844        not(any(
845            target_feature = "v8",
846            atomic_maybe_uninit_target_feature = "v8",
847            target_feature = "v8m",
848            atomic_maybe_uninit_target_feature = "v8m",
849        )),
850    ),
851    path = "arch/arm.rs"
852)]
853#[cfg_attr(
854    all(
855        target_arch = "arm",
856        // Use arm.rs for pre-v8 Arm.
857        any(
858            target_feature = "v8",
859            atomic_maybe_uninit_target_feature = "v8",
860            target_feature = "v8m",
861            atomic_maybe_uninit_target_feature = "v8m",
862        ),
863    ),
864    path = "arch/armv8.rs"
865)]
866#[cfg_attr(
867    all(target_arch = "avr", atomic_maybe_uninit_unstable_asm_experimental_arch),
868    path = "arch/avr.rs"
869)]
870#[cfg_attr(
871    all(target_arch = "csky", atomic_maybe_uninit_unstable_asm_experimental_arch),
872    path = "arch/csky.rs"
873)]
874#[cfg_attr(
875    all(target_arch = "hexagon", atomic_maybe_uninit_unstable_asm_experimental_arch),
876    path = "arch/hexagon.rs"
877)]
878#[cfg_attr(
879    any(
880        all(target_arch = "loongarch32", not(atomic_maybe_uninit_no_asm)),
881        target_arch = "loongarch64",
882    ),
883    path = "arch/loongarch.rs"
884)]
885#[cfg_attr(
886    all(target_arch = "m68k", atomic_maybe_uninit_unstable_asm_experimental_arch),
887    path = "arch/m68k.rs"
888)]
889#[cfg_attr(
890    all(
891        any(
892            // MIPS-I has no SYNC, so we cannot implement non-relaxed atomics.
893            all(target_arch = "mips", not(atomic_maybe_uninit_no_sync)),
894            target_arch = "mips32r6",
895            target_arch = "mips64",
896            target_arch = "mips64r6",
897        ),
898        atomic_maybe_uninit_unstable_asm_experimental_arch,
899    ),
900    path = "arch/mips.rs"
901)]
902#[cfg_attr(
903    all(target_arch = "msp430", atomic_maybe_uninit_unstable_asm_experimental_arch),
904    path = "arch/msp430.rs"
905)]
906#[cfg_attr(
907    all(any(target_arch = "powerpc", target_arch = "powerpc64"), not(atomic_maybe_uninit_no_asm)),
908    path = "arch/powerpc.rs"
909)]
910#[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), path = "arch/riscv.rs")]
911#[cfg_attr(all(target_arch = "s390x", not(atomic_maybe_uninit_no_asm)), path = "arch/s390x.rs")]
912#[cfg_attr(
913    all(
914        any(
915            // SPARC-V7 has no STBAR, so we cannot implement non-relaxed atomics.
916            all(target_arch = "sparc", not(atomic_maybe_uninit_no_stbar)),
917            target_arch = "sparc64",
918        ),
919        atomic_maybe_uninit_unstable_asm_experimental_arch,
920    ),
921    path = "arch/sparc.rs"
922)]
923#[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), path = "arch/x86.rs")]
924#[cfg_attr(
925    all(target_arch = "xtensa", atomic_maybe_uninit_unstable_asm_experimental_arch),
926    path = "arch/xtensa.rs"
927)]
928#[allow(missing_docs)] // For cfg_* macros.
929mod arch;
930
931mod private {
932    #![allow(missing_debug_implementations)]
933
934    use core::panic::{RefUnwindSafe, UnwindSafe};
935
936    /// This trait is private and cannot be implemented for types outside of `atomic-maybe-uninit`.
937    ///
938    /// # Safety
939    ///
940    /// The implementer must guarantee that `align_of::<Self::Align>() == size_of::<Self>()`.
941    // Auto traits are needed to better docs.
942    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
943    pub unsafe trait PrimitivePriv:
944        Copy + Send + Sync + Unpin + UnwindSafe + RefUnwindSafe
945    {
946        // See _align field of AtomicMaybeUninit.
947        type Align: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe;
948    }
949
950    #[repr(align(1))]
951    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
952    pub struct Align1(#[allow(dead_code)] u8);
953    #[repr(align(2))]
954    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
955    pub struct Align2(#[allow(dead_code)] u16);
956    #[repr(align(4))]
957    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
958    pub struct Align4(#[allow(dead_code)] u32);
959    #[repr(align(8))]
960    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
961    pub struct Align8(#[allow(dead_code)] u64);
962    #[repr(align(16))]
963    #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
964    pub struct Align16(#[allow(dead_code)] u128);
965    #[cfg(target_pointer_width = "16")]
966    pub(crate) type AlignPtr = Align2;
967    #[cfg(target_pointer_width = "32")]
968    pub(crate) type AlignPtr = Align4;
969    #[cfg(target_pointer_width = "64")]
970    pub(crate) type AlignPtr = Align8;
971    #[cfg(target_pointer_width = "128")]
972    pub(crate) type AlignPtr = Align16;
973
974    // Check that all cfg_ macros work.
975    use crate::{
976        AtomicMaybeUninit, cfg_has_atomic_8, cfg_has_atomic_16, cfg_has_atomic_32,
977        cfg_has_atomic_64, cfg_has_atomic_128, cfg_has_atomic_cas, cfg_has_atomic_ptr,
978        cfg_no_atomic_8, cfg_no_atomic_16, cfg_no_atomic_32, cfg_no_atomic_64, cfg_no_atomic_128,
979        cfg_no_atomic_cas, cfg_no_atomic_ptr,
980    };
981    // TODO: make these type aliases public?
982    cfg_has_atomic_8! {
983        type _AtomicMaybeUninitI8 = AtomicMaybeUninit<i8>;
984        type _AtomicMaybeUninitU8 = AtomicMaybeUninit<u8>;
985    }
986    cfg_no_atomic_8! {
987        type _AtomicMaybeUninitI8 = AtomicMaybeUninit<i8>;
988        type _AtomicMaybeUninitU8 = AtomicMaybeUninit<u8>;
989    }
990    cfg_has_atomic_16! {
991        type _AtomicMaybeUninitI16 = AtomicMaybeUninit<i16>;
992        type _AtomicMaybeUninitU16 = AtomicMaybeUninit<u16>;
993    }
994    cfg_no_atomic_16! {
995        type _AtomicMaybeUninitI16 = AtomicMaybeUninit<i16>;
996        type _AtomicMaybeUninitU16 = AtomicMaybeUninit<u16>;
997    }
998    cfg_has_atomic_32! {
999        type _AtomicMaybeUninitI32 = AtomicMaybeUninit<i32>;
1000        type _AtomicMaybeUninitU32 = AtomicMaybeUninit<u32>;
1001    }
1002    cfg_no_atomic_32! {
1003        type _AtomicMaybeUninitI32 = AtomicMaybeUninit<i32>;
1004        type _AtomicMaybeUninitU32 = AtomicMaybeUninit<u32>;
1005    }
1006    cfg_has_atomic_64! {
1007        type _AtomicMaybeUninitI64 = AtomicMaybeUninit<i64>;
1008        type _AtomicMaybeUninitU64 = AtomicMaybeUninit<u64>;
1009    }
1010    cfg_no_atomic_64! {
1011        type _AtomicMaybeUninitI64 = AtomicMaybeUninit<i64>;
1012        type _AtomicMaybeUninitU64 = AtomicMaybeUninit<u64>;
1013    }
1014    cfg_has_atomic_128! {
1015        type _AtomicMaybeUninitI128 = AtomicMaybeUninit<i128>;
1016        type _AtomicMaybeUninitU128 = AtomicMaybeUninit<u128>;
1017    }
1018    cfg_no_atomic_128! {
1019        type _AtomicMaybeUninitI128 = AtomicMaybeUninit<i128>;
1020        type _AtomicMaybeUninitU128 = AtomicMaybeUninit<u128>;
1021    }
1022    cfg_has_atomic_ptr! {
1023        type _AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1024        type _AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1025    }
1026    cfg_no_atomic_ptr! {
1027        type _AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1028        type _AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1029    }
1030    cfg_has_atomic_cas! {
1031        type __AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1032        type __AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1033    }
1034    cfg_no_atomic_cas! {
1035        type __AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1036        type __AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1037    }
1038}