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