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