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