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