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 \[13] | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓ |
42| mips64 / mips64r6 \[13] | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ |
43| sparc \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] |
44| sparc (+v8plus) \[8] \[13] (experimental) | i64,u64 | ✓ | ✓ |
45| sparc64 \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ |
46| avr \[13] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ |
47| msp430 \[13] (experimental) | isize,usize,i8,u8,i16,u16 | ✓ | ✓ |
48| hexagon \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ |
49| m68k \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] |
50| m68k (+isa-68020) \[9] \[13] (experimental) | i64,u64 | ✓ | ✓ |
51| csky \[13] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] |
52| xtensa \[13] (experimental) | 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 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, ¤t) {
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 atomic_maybe_uninit_target_feature = "v8",
847 target_feature = "v8m",
848 atomic_maybe_uninit_target_feature = "v8m",
849 )),
850 ),
851 path = "arch/arm.rs"
852)]
853#[cfg_attr(
854 all(
855 target_arch = "arm",
856 // Use arm.rs for pre-v8 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 // MIPS-I has no SYNC, so we cannot implement non-relaxed atomics.
893 all(target_arch = "mips", not(atomic_maybe_uninit_no_sync)),
894 target_arch = "mips32r6",
895 target_arch = "mips64",
896 target_arch = "mips64r6",
897 ),
898 atomic_maybe_uninit_unstable_asm_experimental_arch,
899 ),
900 path = "arch/mips.rs"
901)]
902#[cfg_attr(
903 all(target_arch = "msp430", atomic_maybe_uninit_unstable_asm_experimental_arch),
904 path = "arch/msp430.rs"
905)]
906#[cfg_attr(
907 all(any(target_arch = "powerpc", target_arch = "powerpc64"), not(atomic_maybe_uninit_no_asm)),
908 path = "arch/powerpc.rs"
909)]
910#[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), path = "arch/riscv.rs")]
911#[cfg_attr(all(target_arch = "s390x", not(atomic_maybe_uninit_no_asm)), path = "arch/s390x.rs")]
912#[cfg_attr(
913 all(
914 any(
915 // SPARC-V7 has no STBAR, so we cannot implement non-relaxed atomics.
916 all(target_arch = "sparc", not(atomic_maybe_uninit_no_stbar)),
917 target_arch = "sparc64",
918 ),
919 atomic_maybe_uninit_unstable_asm_experimental_arch,
920 ),
921 path = "arch/sparc.rs"
922)]
923#[cfg_attr(any(target_arch = "x86", target_arch = "x86_64"), path = "arch/x86.rs")]
924#[cfg_attr(
925 all(target_arch = "xtensa", atomic_maybe_uninit_unstable_asm_experimental_arch),
926 path = "arch/xtensa.rs"
927)]
928#[allow(missing_docs)] // For cfg_* macros.
929mod arch;
930
931mod private {
932 #![allow(missing_debug_implementations)]
933
934 use core::panic::{RefUnwindSafe, UnwindSafe};
935
936 /// This trait is private and cannot be implemented for types outside of `atomic-maybe-uninit`.
937 ///
938 /// # Safety
939 ///
940 /// The implementer must guarantee that `align_of::<Self::Align>() == size_of::<Self>()`.
941 // Auto traits are needed to better docs.
942 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
943 pub unsafe trait PrimitivePriv:
944 Copy + Send + Sync + Unpin + UnwindSafe + RefUnwindSafe
945 {
946 // See _align field of AtomicMaybeUninit.
947 type Align: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe;
948 }
949
950 #[repr(align(1))]
951 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
952 pub struct Align1(#[allow(dead_code)] u8);
953 #[repr(align(2))]
954 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
955 pub struct Align2(#[allow(dead_code)] u16);
956 #[repr(align(4))]
957 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
958 pub struct Align4(#[allow(dead_code)] u32);
959 #[repr(align(8))]
960 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
961 pub struct Align8(#[allow(dead_code)] u64);
962 #[repr(align(16))]
963 #[allow(unknown_lints, unnameable_types)] // Not public API. unnameable_types is available on Rust 1.79+
964 pub struct Align16(#[allow(dead_code)] u128);
965 #[cfg(target_pointer_width = "16")]
966 pub(crate) type AlignPtr = Align2;
967 #[cfg(target_pointer_width = "32")]
968 pub(crate) type AlignPtr = Align4;
969 #[cfg(target_pointer_width = "64")]
970 pub(crate) type AlignPtr = Align8;
971 #[cfg(target_pointer_width = "128")]
972 pub(crate) type AlignPtr = Align16;
973
974 // Check that all cfg_ macros work.
975 use crate::{
976 AtomicMaybeUninit, cfg_has_atomic_8, cfg_has_atomic_16, cfg_has_atomic_32,
977 cfg_has_atomic_64, cfg_has_atomic_128, cfg_has_atomic_cas, cfg_has_atomic_ptr,
978 cfg_no_atomic_8, cfg_no_atomic_16, cfg_no_atomic_32, cfg_no_atomic_64, cfg_no_atomic_128,
979 cfg_no_atomic_cas, cfg_no_atomic_ptr,
980 };
981 // TODO: make these type aliases public?
982 cfg_has_atomic_8! {
983 type _AtomicMaybeUninitI8 = AtomicMaybeUninit<i8>;
984 type _AtomicMaybeUninitU8 = AtomicMaybeUninit<u8>;
985 }
986 cfg_no_atomic_8! {
987 type _AtomicMaybeUninitI8 = AtomicMaybeUninit<i8>;
988 type _AtomicMaybeUninitU8 = AtomicMaybeUninit<u8>;
989 }
990 cfg_has_atomic_16! {
991 type _AtomicMaybeUninitI16 = AtomicMaybeUninit<i16>;
992 type _AtomicMaybeUninitU16 = AtomicMaybeUninit<u16>;
993 }
994 cfg_no_atomic_16! {
995 type _AtomicMaybeUninitI16 = AtomicMaybeUninit<i16>;
996 type _AtomicMaybeUninitU16 = AtomicMaybeUninit<u16>;
997 }
998 cfg_has_atomic_32! {
999 type _AtomicMaybeUninitI32 = AtomicMaybeUninit<i32>;
1000 type _AtomicMaybeUninitU32 = AtomicMaybeUninit<u32>;
1001 }
1002 cfg_no_atomic_32! {
1003 type _AtomicMaybeUninitI32 = AtomicMaybeUninit<i32>;
1004 type _AtomicMaybeUninitU32 = AtomicMaybeUninit<u32>;
1005 }
1006 cfg_has_atomic_64! {
1007 type _AtomicMaybeUninitI64 = AtomicMaybeUninit<i64>;
1008 type _AtomicMaybeUninitU64 = AtomicMaybeUninit<u64>;
1009 }
1010 cfg_no_atomic_64! {
1011 type _AtomicMaybeUninitI64 = AtomicMaybeUninit<i64>;
1012 type _AtomicMaybeUninitU64 = AtomicMaybeUninit<u64>;
1013 }
1014 cfg_has_atomic_128! {
1015 type _AtomicMaybeUninitI128 = AtomicMaybeUninit<i128>;
1016 type _AtomicMaybeUninitU128 = AtomicMaybeUninit<u128>;
1017 }
1018 cfg_no_atomic_128! {
1019 type _AtomicMaybeUninitI128 = AtomicMaybeUninit<i128>;
1020 type _AtomicMaybeUninitU128 = AtomicMaybeUninit<u128>;
1021 }
1022 cfg_has_atomic_ptr! {
1023 type _AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1024 type _AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1025 }
1026 cfg_no_atomic_ptr! {
1027 type _AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1028 type _AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1029 }
1030 cfg_has_atomic_cas! {
1031 type __AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1032 type __AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1033 }
1034 cfg_no_atomic_cas! {
1035 type __AtomicMaybeUninitIsize = AtomicMaybeUninit<isize>;
1036 type __AtomicMaybeUninitUsize = AtomicMaybeUninit<usize>;
1037 }
1038}