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