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