Skip to main content

atomic_maybe_uninit/
lib.rs

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