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