#![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,
unsafe_op_in_unsafe_fn
)]
#![warn(
clippy::pedantic,
// lints for public library
clippy::alloc_instead_of_core,
clippy::exhaustive_enums,
clippy::exhaustive_structs,
clippy::std_instead_of_alloc,
clippy::std_instead_of_core,
// lints that help writing unsafe code
clippy::default_union_representation,
clippy::trailing_empty_array,
clippy::transmute_undefined_repr,
clippy::undocumented_unsafe_blocks,
// misc
clippy::inline_asm_x86_att_syntax,
clippy::missing_inline_in_public_items,
)]
#![allow(
clippy::doc_markdown,
clippy::missing_errors_doc,
clippy::module_inception,
clippy::too_many_lines,
clippy::type_complexity
)]
#![cfg_attr(
not(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "arm",
target_arch = "aarch64",
target_arch = "riscv32",
target_arch = "riscv64",
)),
feature(asm_experimental_arch)
)]
#[cfg(test)]
extern crate std;
#[macro_use]
mod utils;
#[cfg(test)]
#[macro_use]
mod tests;
mod arch;
pub mod raw;
#[cfg(doc)]
use core::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release, SeqCst};
use core::{cell::UnsafeCell, fmt, mem::MaybeUninit, sync::atomic::Ordering};
use crate::raw::{AtomicCompareExchange, AtomicLoad, AtomicStore, AtomicSwap, Primitive};
#[repr(C)]
pub struct AtomicMaybeUninit<T: Primitive> {
v: UnsafeCell<MaybeUninit<T>>,
_align: [T::Align; 0],
}
impl<T: Primitive> From<MaybeUninit<T>> for AtomicMaybeUninit<T> {
#[inline]
fn from(v: MaybeUninit<T>) -> Self {
Self::new(v)
}
}
impl<T: Primitive> From<T> for AtomicMaybeUninit<T> {
#[inline]
fn from(v: T) -> Self {
Self::new(MaybeUninit::new(v))
}
}
impl<T: Primitive> fmt::Debug for AtomicMaybeUninit<T> {
#[allow(clippy::missing_inline_in_public_items)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(core::any::type_name::<Self>())
}
}
unsafe impl<T: Primitive> Sync for AtomicMaybeUninit<T> {}
impl<T: Primitive> core::panic::RefUnwindSafe for AtomicMaybeUninit<T> {}
impl<T: Primitive> AtomicMaybeUninit<T> {
#[cfg(not(atomic_maybe_uninit_no_const_fn_trait_bound))]
#[inline]
#[must_use]
pub const fn new(v: MaybeUninit<T>) -> Self {
Self { v: UnsafeCell::new(v), _align: [] }
}
#[cfg(atomic_maybe_uninit_no_const_fn_trait_bound)]
#[inline]
#[must_use]
pub fn new(v: MaybeUninit<T>) -> Self {
Self { v: UnsafeCell::new(v), _align: [] }
}
#[inline]
pub fn get_mut(&mut self) -> &mut MaybeUninit<T> {
self.v.get_mut()
}
#[inline]
pub fn into_inner(self) -> MaybeUninit<T> {
self.v.into_inner()
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn load(&self, order: Ordering) -> MaybeUninit<T>
where
T: AtomicLoad,
{
utils::assert_load_ordering(order);
let mut out = MaybeUninit::<T>::uninit();
unsafe { T::atomic_load(self.v.get(), &mut out, order) }
out
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn store(&self, val: MaybeUninit<T>, order: Ordering)
where
T: AtomicStore,
{
utils::assert_store_ordering(order);
unsafe { T::atomic_store(self.v.get(), &val, order) }
}
#[inline]
pub fn swap(&self, val: MaybeUninit<T>, order: Ordering) -> MaybeUninit<T>
where
T: AtomicSwap,
{
utils::assert_swap_ordering(order);
let mut out = MaybeUninit::<T>::uninit();
unsafe { T::atomic_swap(self.v.get(), &val, &mut out, order) }
out
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn compare_exchange(
&self,
current: MaybeUninit<T>,
new: MaybeUninit<T>,
success: Ordering,
failure: Ordering,
) -> Result<MaybeUninit<T>, MaybeUninit<T>>
where
T: AtomicCompareExchange,
{
utils::assert_compare_exchange_ordering(success, failure);
let mut out = MaybeUninit::<T>::uninit();
let res = unsafe {
T::atomic_compare_exchange(self.v.get(), ¤t, &new, &mut out, success, failure)
};
if res {
Ok(out)
} else {
Err(out)
}
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn compare_exchange_weak(
&self,
current: MaybeUninit<T>,
new: MaybeUninit<T>,
success: Ordering,
failure: Ordering,
) -> Result<MaybeUninit<T>, MaybeUninit<T>>
where
T: AtomicCompareExchange,
{
utils::assert_compare_exchange_ordering(success, failure);
let mut out = MaybeUninit::<T>::uninit();
let res = unsafe {
T::atomic_compare_exchange_weak(
self.v.get(),
¤t,
&new,
&mut out,
success,
failure,
)
};
if res {
Ok(out)
} else {
Err(out)
}
}
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<MaybeUninit<T>, MaybeUninit<T>>
where
F: FnMut(MaybeUninit<T>) -> Option<MaybeUninit<T>>,
T: AtomicCompareExchange,
{
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! int {
($ty:ident, $align:ident) => {
impl crate::raw::Primitive for $ty {}
impl crate::private::PrimitivePriv for $ty {
type Align = crate::private::$align;
}
impl AtomicMaybeUninit<$ty> {
#[inline]
#[must_use]
pub const fn const_new(v: MaybeUninit<$ty>) -> Self {
Self { v: UnsafeCell::new(v), _align: [] }
}
}
static_assert!(
core::mem::size_of::<AtomicMaybeUninit<$ty>>() == core::mem::size_of::<$ty>()
);
static_assert!(
core::mem::align_of::<AtomicMaybeUninit<$ty>>() == core::mem::size_of::<$ty>()
);
};
}
int!(i8, Align1);
int!(u8, Align1);
int!(i16, Align2);
int!(u16, Align2);
int!(i32, Align4);
int!(u32, Align4);
int!(i64, Align8);
int!(u64, Align8);
int!(i128, Align16);
int!(u128, Align16);
int!(isize, AlignPtr);
int!(usize, AlignPtr);
mod private {
use core::panic::{RefUnwindSafe, UnwindSafe};
pub trait PrimitivePriv: Copy + Send + Sync + Unpin + UnwindSafe + RefUnwindSafe {
type Align: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe;
}
#[allow(missing_debug_implementations)]
#[repr(align(1))]
pub struct Align1(u8);
#[allow(missing_debug_implementations)]
#[repr(align(2))]
pub struct Align2(u16);
#[allow(missing_debug_implementations)]
#[repr(align(4))]
pub struct Align4(u32);
#[allow(missing_debug_implementations)]
#[repr(align(8))]
pub struct Align8(u64);
#[allow(missing_debug_implementations)]
#[repr(align(16))]
pub struct Align16(u128);
#[cfg(target_pointer_width = "16")]
pub(crate) type AlignPtr = Align2;
#[cfg(target_pointer_width = "32")]
pub(crate) type AlignPtr = Align4;
#[cfg(target_pointer_width = "64")]
pub(crate) type AlignPtr = Align8;
#[cfg(target_pointer_width = "128")]
pub(crate) type AlignPtr = Align16;
}