#![no_std]
#![doc(test(
no_crate_inject,
attr(
deny(warnings, rust_2018_idioms, single_use_lifetimes),
allow(dead_code, unused_variables)
)
))]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
single_use_lifetimes,
unreachable_pub
)]
#![cfg_attr(not(portable_atomic_no_unsafe_op_in_unsafe_fn), warn(unsafe_op_in_unsafe_fn))] #![cfg_attr(portable_atomic_no_unsafe_op_in_unsafe_fn, allow(unused_unsafe))]
#![warn(
clippy::default_union_representation,
clippy::exhaustive_enums,
clippy::exhaustive_structs,
clippy::inline_asm_x86_att_syntax,
clippy::missing_inline_in_public_items,
clippy::pedantic,
clippy::transmute_undefined_repr,
clippy::undocumented_unsafe_blocks
)]
#![allow(
clippy::cast_lossless,
clippy::doc_markdown,
clippy::float_cmp,
clippy::inline_always,
clippy::missing_errors_doc,
clippy::module_inception,
clippy::type_complexity
)]
#![cfg_attr(
all(
target_arch = "x86_64",
any(
all(test, portable_atomic_nightly),
portable_atomic_cmpxchg16b_stdsimd,
miri,
sanitize_thread
)
),
feature(stdsimd, cmpxchg16b_target_feature)
)]
#![cfg_attr(portable_atomic_unstable_cfg_target_has_atomic, feature(cfg_target_has_atomic))]
#![cfg_attr(
all(
portable_atomic_nightly,
not(portable_atomic_no_asm),
any(
target_arch = "avr",
target_arch = "msp430",
target_arch = "powerpc64",
target_arch = "s390x",
),
),
feature(asm_experimental_arch)
)]
#![cfg_attr(
all(
portable_atomic_nightly,
portable_atomic_no_asm,
any(
portable_atomic_armv6m,
all(target_arch = "riscv32", portable_atomic_no_atomic_load_store),
target_arch = "aarch64",
target_arch = "x86_64",
),
),
feature(asm)
)]
#![cfg_attr(
all(any(target_arch = "avr", target_arch = "msp430"), portable_atomic_no_asm),
feature(llvm_asm)
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(not(any(
target_pointer_width = "16",
target_pointer_width = "32",
target_pointer_width = "64",
)))]
compile_error!(
"portable-atomic currently only supports targets with {16,32,64}-bit pointer width; \
if you need support for others, \
please submit an issue at <https://github.com/taiki-e/portable-atomic>"
);
#[cfg(portable_atomic_unsafe_assume_single_core)]
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
not(any(portable_atomic_armv6m, target_arch = "riscv32", target_pointer_width = "16"))
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(
target_has_atomic = "ptr",
not(any(portable_atomic_armv6m, target_arch = "riscv32", target_pointer_width = "16"))
))
)]
compile_error!(
"cfg(portable_atomic_unsafe_assume_single_core) does not compatible with this target; \
if you need cfg(portable_atomic_unsafe_assume_single_core) support for this target, \
please submit an issue at <https://github.com/taiki-e/portable-atomic>"
);
#[cfg(any(test, feature = "std"))]
extern crate std;
#[macro_use]
mod utils;
#[cfg(test)]
#[macro_use]
mod tests;
#[doc(no_inline)]
pub use core::sync::atomic::{compiler_fence, fence, Ordering};
mod imp;
pub mod hint {
#[doc(no_inline)]
pub use core::hint::*;
#[inline]
pub fn spin_loop() {
#[allow(deprecated)]
core::sync::atomic::spin_loop_hint();
}
}
#[cfg(doc)]
use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
use core::{fmt, marker::PhantomData, ptr};
use crate::utils::NoRefUnwindSafe;
#[repr(C, align(1))]
pub struct AtomicBool {
inner: imp::AtomicBool,
_marker: PhantomData<NoRefUnwindSafe>,
}
static_assert_layout!(AtomicBool, bool);
impl Default for AtomicBool {
#[inline]
fn default() -> Self {
Self::new(false)
}
}
impl From<bool> for AtomicBool {
#[inline]
fn from(b: bool) -> Self {
Self::new(b)
}
}
impl fmt::Debug for AtomicBool {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.load(Ordering::Relaxed), f)
}
}
#[cfg(not(portable_atomic_no_core_unwind_safe))]
impl core::panic::RefUnwindSafe for AtomicBool {}
#[cfg(all(portable_atomic_no_core_unwind_safe, feature = "std"))]
impl std::panic::RefUnwindSafe for AtomicBool {}
serde_impls!(AtomicBool);
impl AtomicBool {
#[inline]
#[must_use]
pub const fn new(v: bool) -> Self {
Self { inner: imp::AtomicBool::new(v), _marker: PhantomData }
}
#[inline]
#[must_use]
pub fn is_lock_free() -> bool {
imp::AtomicBool::is_lock_free()
}
#[inline]
#[must_use]
pub const fn is_always_lock_free() -> bool {
imp::AtomicBool::is_always_lock_free()
}
#[inline]
pub fn get_mut(&mut self) -> &mut bool {
self.inner.get_mut()
}
#[inline]
pub fn into_inner(self) -> bool {
self.inner.into_inner()
}
#[inline]
pub fn load(&self, order: Ordering) -> bool {
self.inner.load(order)
}
#[inline]
pub fn store(&self, val: bool, order: Ordering) {
self.inner.store(val, order);
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn swap(&self, val: bool, order: Ordering) -> bool {
self.inner.swap(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange(
&self,
current: bool,
new: bool,
success: Ordering,
failure: Ordering,
) -> Result<bool, bool> {
self.inner.compare_exchange(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange_weak(
&self,
current: bool,
new: bool,
success: Ordering,
failure: Ordering,
) -> Result<bool, bool> {
self.inner.compare_exchange_weak(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_and(&self, val: bool, order: Ordering) -> bool {
self.inner.fetch_and(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool {
self.inner.fetch_nand(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_or(&self, val: bool, order: Ordering) -> bool {
self.inner.fetch_or(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool {
self.inner.fetch_xor(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<bool, bool>
where
F: FnMut(bool) -> Option<bool>,
{
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
x @ Ok(_) => return x,
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
}
}
#[cfg_attr(target_pointer_width = "16", repr(C, align(2)))]
#[cfg_attr(target_pointer_width = "32", repr(C, align(4)))]
#[cfg_attr(target_pointer_width = "64", repr(C, align(8)))]
#[cfg_attr(target_pointer_width = "128", repr(C, align(16)))]
pub struct AtomicPtr<T> {
inner: imp::AtomicPtr<T>,
_marker: PhantomData<NoRefUnwindSafe>,
}
static_assert_layout!(AtomicPtr<()>, *mut ());
impl<T> Default for AtomicPtr<T> {
#[inline]
fn default() -> Self {
Self::new(ptr::null_mut())
}
}
impl<T> From<*mut T> for AtomicPtr<T> {
#[inline]
fn from(p: *mut T) -> Self {
Self::new(p)
}
}
impl<T> fmt::Debug for AtomicPtr<T> {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.load(Ordering::Relaxed), f)
}
}
impl<T> fmt::Pointer for AtomicPtr<T> {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.load(Ordering::Relaxed), f)
}
}
#[cfg(not(portable_atomic_no_core_unwind_safe))]
impl<T> core::panic::RefUnwindSafe for AtomicPtr<T> {}
#[cfg(all(portable_atomic_no_core_unwind_safe, feature = "std"))]
impl<T> std::panic::RefUnwindSafe for AtomicPtr<T> {}
impl<T> AtomicPtr<T> {
#[inline]
#[must_use]
pub const fn new(p: *mut T) -> Self {
Self { inner: imp::AtomicPtr::new(p), _marker: PhantomData }
}
#[inline]
#[must_use]
pub fn is_lock_free() -> bool {
<imp::AtomicPtr<T>>::is_lock_free()
}
#[inline]
#[must_use]
pub const fn is_always_lock_free() -> bool {
<imp::AtomicPtr<T>>::is_always_lock_free()
}
#[inline]
pub fn get_mut(&mut self) -> &mut *mut T {
self.inner.get_mut()
}
#[inline]
pub fn into_inner(self) -> *mut T {
self.inner.into_inner()
}
#[inline]
pub fn load(&self, order: Ordering) -> *mut T {
self.inner.load(order)
}
#[inline]
pub fn store(&self, ptr: *mut T, order: Ordering) {
self.inner.store(ptr, order);
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T {
self.inner.swap(ptr, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange(
&self,
current: *mut T,
new: *mut T,
success: Ordering,
failure: Ordering,
) -> Result<*mut T, *mut T> {
self.inner.compare_exchange(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange_weak(
&self,
current: *mut T,
new: *mut T,
success: Ordering,
failure: Ordering,
) -> Result<*mut T, *mut T> {
self.inner.compare_exchange_weak(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<*mut T, *mut T>
where
F: FnMut(*mut T) -> Option<*mut T>,
{
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
x @ Ok(_) => return x,
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
}
}
macro_rules! atomic_int {
(AtomicU32, $int_type:ident, $align:expr) => {
atomic_int!(@int, AtomicU32, $int_type, $align);
#[cfg(feature = "float")]
atomic_int!(@float, AtomicF32, f32, AtomicU32, $int_type, $align);
};
(AtomicU64, $int_type:ident, $align:expr) => {
atomic_int!(@int, AtomicU64, $int_type, $align);
#[cfg(feature = "float")]
atomic_int!(@float, AtomicF64, f64, AtomicU64, $int_type, $align);
};
($atomic_type:ident, $int_type:ident, $align:expr) => {
atomic_int!(@int, $atomic_type, $int_type, $align);
};
(@int,
$atomic_type:ident, $int_type:ident, $align:expr
) => {
doc_comment! {
concat!("An integer type which can be safely shared between threads.
This type has the same in-memory representation as the underlying integer type,
[`", stringify!($int_type), "`].
If the compiler or the platform supports atomic loads and stores of [`", stringify!($int_type),
"`], this type is a wrapper for the standard library's `", stringify!($atomic_type),
"`, otherwise synchronizes using global locks.
You can call [`", stringify!($atomic_type), "::is_lock_free()`] to check whether
atomic instructions or locks will be used.
"
),
#[repr(C, align($align))]
pub struct $atomic_type {
inner: imp::$atomic_type,
_marker: PhantomData<NoRefUnwindSafe>,
}
}
static_assert_layout!($atomic_type, $int_type);
impl Default for $atomic_type {
#[inline]
fn default() -> Self {
Self::new($int_type::default())
}
}
impl From<$int_type> for $atomic_type {
#[inline]
fn from(v: $int_type) -> Self {
Self::new(v)
}
}
impl fmt::Debug for $atomic_type {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.load(Ordering::Relaxed), f)
}
}
#[cfg(not(portable_atomic_no_core_unwind_safe))]
impl core::panic::RefUnwindSafe for $atomic_type {}
#[cfg(all(portable_atomic_no_core_unwind_safe, feature = "std"))]
impl std::panic::RefUnwindSafe for $atomic_type {}
serde_impls!($atomic_type);
impl $atomic_type {
#[inline]
#[must_use]
pub const fn new(v: $int_type) -> Self {
Self { inner: imp::$atomic_type::new(v), _marker: PhantomData }
}
#[inline]
#[must_use]
pub fn is_lock_free() -> bool {
<imp::$atomic_type>::is_lock_free()
}
#[inline]
#[must_use]
pub const fn is_always_lock_free() -> bool {
<imp::$atomic_type>::is_always_lock_free()
}
#[inline]
pub fn get_mut(&mut self) -> &mut $int_type {
self.inner.get_mut()
}
#[inline]
pub fn into_inner(self) -> $int_type {
self.inner.into_inner()
}
#[inline]
pub fn load(&self, order: Ordering) -> $int_type {
self.inner.load(order)
}
#[inline]
pub fn store(&self, val: $int_type, order: Ordering) {
self.inner.store(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.swap(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange(
&self,
current: $int_type,
new: $int_type,
success: Ordering,
failure: Ordering,
) -> Result<$int_type, $int_type> {
self.inner.compare_exchange(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange_weak(
&self,
current: $int_type,
new: $int_type,
success: Ordering,
failure: Ordering,
) -> Result<$int_type, $int_type> {
self.inner.compare_exchange_weak(current, new, success, failure)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_add(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_add(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_sub(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_sub(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_and(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_and(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_nand(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_nand(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_or(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_or(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_xor(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_xor(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<$int_type, $int_type>
where
F: FnMut($int_type) -> Option<$int_type>,
{
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
x @ Ok(_) => return x,
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_max(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_max(val, order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_min(&self, val: $int_type, order: Ordering) -> $int_type {
self.inner.fetch_min(val, order)
}
}
};
(@float,
$atomic_type:ident, $float_type:ident, $atomic_int_type:ident, $int_type:ident, $align:expr
) => {
doc_comment! {
concat!("A floating point type which can be safely shared between threads.
This type has the same in-memory representation as the underlying floating point type,
[`", stringify!($float_type), "`].
"
),
#[cfg_attr(docsrs, doc(cfg(feature = "float")))]
#[repr(C, align($align))]
pub struct $atomic_type {
v: core::cell::UnsafeCell<$float_type>,
}
}
static_assert_layout!($atomic_type, $float_type);
impl Default for $atomic_type {
#[inline]
fn default() -> Self {
Self::new($float_type::default())
}
}
impl From<$float_type> for $atomic_type {
#[inline]
fn from(v: $float_type) -> Self {
Self::new(v)
}
}
impl fmt::Debug for $atomic_type {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.load(Ordering::Relaxed), f)
}
}
unsafe impl Sync for $atomic_type {}
#[cfg(not(portable_atomic_no_core_unwind_safe))]
impl core::panic::RefUnwindSafe for $atomic_type {}
#[cfg(all(portable_atomic_no_core_unwind_safe, feature = "std"))]
impl std::panic::RefUnwindSafe for $atomic_type {}
serde_impls!($atomic_type);
impl $atomic_type {
#[inline]
#[must_use]
pub const fn new(v: $float_type) -> Self {
Self { v: core::cell::UnsafeCell::new(v) }
}
#[inline]
#[must_use]
pub fn is_lock_free() -> bool {
crate::$atomic_int_type::is_lock_free()
}
#[inline]
#[must_use]
pub const fn is_always_lock_free() -> bool {
crate::$atomic_int_type::is_always_lock_free()
}
#[inline]
pub fn get_mut(&mut self) -> &mut $float_type {
unsafe { &mut *self.v.get() }
}
#[inline]
pub fn into_inner(self) -> $float_type {
self.v.into_inner()
}
#[inline]
pub fn load(&self, order: Ordering) -> $float_type {
$float_type::from_bits(self.as_bits().load(order))
}
#[inline]
pub fn store(&self, val: $float_type, order: Ordering) {
self.as_bits().store(val.to_bits(), order)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn swap(&self, val: $float_type, order: Ordering) -> $float_type {
$float_type::from_bits(self.as_bits().swap(val.to_bits(), order))
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange(
&self,
current: $float_type,
new: $float_type,
success: Ordering,
failure: Ordering,
) -> Result<$float_type, $float_type> {
match self.as_bits().compare_exchange(
current.to_bits(),
new.to_bits(),
success,
failure,
) {
Ok(v) => Ok($float_type::from_bits(v)),
Err(v) => Err($float_type::from_bits(v)),
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
#[cfg_attr(docsrs, doc(alias = "compare_and_swap"))]
pub fn compare_exchange_weak(
&self,
current: $float_type,
new: $float_type,
success: Ordering,
failure: Ordering,
) -> Result<$float_type, $float_type> {
match self.as_bits().compare_exchange_weak(
current.to_bits(),
new.to_bits(),
success,
failure,
) {
Ok(v) => Ok($float_type::from_bits(v)),
Err(v) => Err($float_type::from_bits(v)),
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_add(&self, val: $float_type, order: Ordering) -> $float_type {
self.fetch_update(order, crate::utils::strongest_failure_ordering(order), |x| {
Some(x + val)
})
.unwrap()
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_sub(&self, val: $float_type, order: Ordering) -> $float_type {
self.fetch_update(order, crate::utils::strongest_failure_ordering(order), |x| {
Some(x - val)
})
.unwrap()
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<$float_type, $float_type>
where
F: FnMut($float_type) -> Option<$float_type>,
{
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
match self.compare_exchange_weak(prev, next, set_order, fetch_order) {
x @ Ok(_) => return x,
Err(next_prev) => prev = next_prev,
}
}
Err(prev)
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_max(&self, val: $float_type, order: Ordering) -> $float_type {
self.fetch_update(order, crate::utils::strongest_failure_ordering(order), |x| {
Some(x.max(val))
})
.unwrap()
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_min(&self, val: $float_type, order: Ordering) -> $float_type {
self.fetch_update(order, crate::utils::strongest_failure_ordering(order), |x| {
Some(x.min(val))
})
.unwrap()
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
#[inline]
pub fn fetch_abs(&self, order: Ordering) -> $float_type {
const ABS_MASK: $int_type = !0 / 2;
$float_type::from_bits(self.as_bits().fetch_and(ABS_MASK, order))
}
doc_comment! {
concat!("Raw transmutation to `", stringify!($atomic_int_type), "`.
See [`", stringify!($float_type) ,"::from_bits`] for some discussion of the
portability of this operation (there are almost no issues)."),
#[inline]
pub fn as_bits(&self) -> &crate::$atomic_int_type {
unsafe { &*(self as *const $atomic_type as *const crate::$atomic_int_type) }
}
}
}
};
}
#[cfg(target_pointer_width = "16")]
atomic_int!(AtomicIsize, isize, 2);
#[cfg(target_pointer_width = "16")]
atomic_int!(AtomicUsize, usize, 2);
#[cfg(target_pointer_width = "32")]
atomic_int!(AtomicIsize, isize, 4);
#[cfg(target_pointer_width = "32")]
atomic_int!(AtomicUsize, usize, 4);
#[cfg(target_pointer_width = "64")]
atomic_int!(AtomicIsize, isize, 8);
#[cfg(target_pointer_width = "64")]
atomic_int!(AtomicUsize, usize, 8);
#[cfg(target_pointer_width = "128")]
atomic_int!(AtomicIsize, isize, 16);
#[cfg(target_pointer_width = "128")]
atomic_int!(AtomicUsize, usize, 16);
atomic_int!(AtomicI8, i8, 1);
atomic_int!(AtomicU8, u8, 1);
atomic_int!(AtomicI16, i16, 2);
atomic_int!(AtomicU16, u16, 2);
#[cfg(any(
not(target_pointer_width = "16"),
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))]
atomic_int!(AtomicI32, i32, 4);
#[cfg(any(
not(target_pointer_width = "16"),
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))]
atomic_int!(AtomicU32, u32, 4);
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
all(feature = "fallback", not(portable_atomic_no_atomic_cas)),
not(portable_atomic_no_atomic_64),
target_pointer_width = "64",
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(
all(feature = "fallback", target_has_atomic = "ptr"),
target_has_atomic = "64",
target_pointer_width = "64",
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))
)]
atomic_int!(AtomicI64, i64, 8);
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
all(feature = "fallback", not(portable_atomic_no_atomic_cas)),
not(portable_atomic_no_atomic_64),
target_pointer_width = "64",
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(
all(feature = "fallback", target_has_atomic = "ptr"),
target_has_atomic = "64",
target_pointer_width = "64",
all(feature = "fallback", portable_atomic_unsafe_assume_single_core)
))
)]
atomic_int!(AtomicU64, u64, 8);
#[cfg_attr(
not(feature = "fallback"),
cfg(any(
all(any(not(portable_atomic_no_asm), portable_atomic_nightly), target_arch = "aarch64"),
all(
any(not(portable_atomic_no_asm), portable_atomic_nightly),
any(
target_feature = "cmpxchg16b",
portable_atomic_target_feature = "cmpxchg16b",
portable_atomic_cmpxchg16b_dynamic
),
target_arch = "x86_64",
),
all(
all(not(portable_atomic_no_asm), portable_atomic_nightly),
any(
target_endian = "little",
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics"
),
target_arch = "powerpc64"
),
all(all(not(portable_atomic_no_asm), portable_atomic_nightly), target_arch = "s390x"),
))
)]
#[cfg_attr(
all(feature = "fallback", portable_atomic_no_cfg_target_has_atomic),
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
all(feature = "fallback", not(portable_atomic_no_cfg_target_has_atomic)),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
atomic_int!(AtomicI128, i128, 16);
#[cfg_attr(
not(feature = "fallback"),
cfg(any(
all(any(not(portable_atomic_no_asm), portable_atomic_nightly), target_arch = "aarch64"),
all(
any(not(portable_atomic_no_asm), portable_atomic_nightly),
any(
target_feature = "cmpxchg16b",
portable_atomic_target_feature = "cmpxchg16b",
portable_atomic_cmpxchg16b_dynamic
),
target_arch = "x86_64",
),
all(
all(not(portable_atomic_no_asm), portable_atomic_nightly),
any(
target_endian = "little",
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics"
),
target_arch = "powerpc64"
),
all(all(not(portable_atomic_no_asm), portable_atomic_nightly), target_arch = "s390x"),
))
)]
#[cfg_attr(
all(feature = "fallback", portable_atomic_no_cfg_target_has_atomic),
cfg(any(not(portable_atomic_no_atomic_cas), portable_atomic_unsafe_assume_single_core))
)]
#[cfg_attr(
all(feature = "fallback", not(portable_atomic_no_cfg_target_has_atomic)),
cfg(any(target_has_atomic = "ptr", portable_atomic_unsafe_assume_single_core))
)]
atomic_int!(AtomicU128, u128, 16);