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