mod internal;
pub mod ordering;
mod predefine;
pub use internal::AtomicImpl;
pub use ordering::{Acquire, Full, Relaxed, Release};
pub(crate) use internal::{AtomicArithmeticOps, AtomicBasicOps, AtomicExchangeOps};
use crate::build_error;
use internal::AtomicRepr;
use ordering::OrderingType;
#[repr(transparent)]
pub struct Atomic<T: AtomicType>(AtomicRepr<T::Repr>);
unsafe impl<T: AtomicType> Send for Atomic<T> {}
unsafe impl<T: AtomicType> Sync for Atomic<T> {}
pub unsafe trait AtomicType: Sized + Copy {
type Repr: AtomicImpl;
}
pub unsafe trait AtomicAdd<Rhs = Self>: AtomicType {
fn rhs_into_delta(rhs: Rhs) -> <Self::Repr as AtomicImpl>::Delta;
}
#[inline(always)]
const fn into_repr<T: AtomicType>(v: T) -> T::Repr {
unsafe { core::mem::transmute_copy(&v) }
}
#[inline(always)]
const unsafe fn from_repr<T: AtomicType>(r: T::Repr) -> T {
unsafe { core::mem::transmute_copy(&r) }
}
impl<T: AtomicType> Atomic<T> {
pub const fn new(v: T) -> Self {
Self(AtomicRepr::new(into_repr(v)))
}
pub unsafe fn from_ptr<'a>(ptr: *mut T) -> &'a Self {
unsafe { &*ptr.cast::<Self>() }
}
pub const fn as_ptr(&self) -> *mut T {
self.0.as_ptr().cast()
}
pub fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.as_ptr().cast() }
}
}
impl<T: AtomicType> Atomic<T>
where
T::Repr: AtomicBasicOps,
{
#[doc(alias("atomic_read", "atomic64_read"))]
#[inline(always)]
pub fn load<Ordering: ordering::AcquireOrRelaxed>(&self, _: Ordering) -> T {
let v = {
match Ordering::TYPE {
OrderingType::Relaxed => T::Repr::atomic_read(&self.0),
OrderingType::Acquire => T::Repr::atomic_read_acquire(&self.0),
_ => build_error!("Wrong ordering"),
}
};
unsafe { from_repr(v) }
}
#[doc(alias("atomic_set", "atomic64_set"))]
#[inline(always)]
pub fn store<Ordering: ordering::ReleaseOrRelaxed>(&self, v: T, _: Ordering) {
let v = into_repr(v);
match Ordering::TYPE {
OrderingType::Relaxed => T::Repr::atomic_set(&self.0, v),
OrderingType::Release => T::Repr::atomic_set_release(&self.0, v),
_ => build_error!("Wrong ordering"),
}
}
}
impl<T: AtomicType + core::fmt::Debug> core::fmt::Debug for Atomic<T>
where
T::Repr: AtomicBasicOps,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(&self.load(Relaxed), f)
}
}
impl<T: AtomicType> Atomic<T>
where
T::Repr: AtomicExchangeOps,
{
#[doc(alias("atomic_xchg", "atomic64_xchg", "swap"))]
#[inline(always)]
pub fn xchg<Ordering: ordering::Ordering>(&self, v: T, _: Ordering) -> T {
let v = into_repr(v);
let ret = {
match Ordering::TYPE {
OrderingType::Full => T::Repr::atomic_xchg(&self.0, v),
OrderingType::Acquire => T::Repr::atomic_xchg_acquire(&self.0, v),
OrderingType::Release => T::Repr::atomic_xchg_release(&self.0, v),
OrderingType::Relaxed => T::Repr::atomic_xchg_relaxed(&self.0, v),
}
};
unsafe { from_repr(ret) }
}
#[doc(alias(
"atomic_cmpxchg",
"atomic64_cmpxchg",
"atomic_try_cmpxchg",
"atomic64_try_cmpxchg",
"compare_exchange"
))]
#[inline(always)]
pub fn cmpxchg<Ordering: ordering::Ordering>(
&self,
mut old: T,
new: T,
o: Ordering,
) -> Result<T, T> {
if self.try_cmpxchg(&mut old, new, o) {
Ok(old)
} else {
Err(old)
}
}
#[inline(always)]
fn try_cmpxchg<Ordering: ordering::Ordering>(&self, old: &mut T, new: T, _: Ordering) -> bool {
let mut tmp = into_repr(*old);
let new = into_repr(new);
let ret = {
match Ordering::TYPE {
OrderingType::Full => T::Repr::atomic_try_cmpxchg(&self.0, &mut tmp, new),
OrderingType::Acquire => {
T::Repr::atomic_try_cmpxchg_acquire(&self.0, &mut tmp, new)
}
OrderingType::Release => {
T::Repr::atomic_try_cmpxchg_release(&self.0, &mut tmp, new)
}
OrderingType::Relaxed => {
T::Repr::atomic_try_cmpxchg_relaxed(&self.0, &mut tmp, new)
}
}
};
*old = unsafe { from_repr(tmp) };
ret
}
}
impl<T: AtomicType> Atomic<T>
where
T::Repr: AtomicArithmeticOps,
{
#[inline(always)]
pub fn add<Rhs>(&self, v: Rhs, _: ordering::Relaxed)
where
T: AtomicAdd<Rhs>,
{
let v = T::rhs_into_delta(v);
T::Repr::atomic_add(&self.0, v);
}
#[inline(always)]
pub fn fetch_add<Rhs, Ordering: ordering::Ordering>(&self, v: Rhs, _: Ordering) -> T
where
T: AtomicAdd<Rhs>,
{
let v = T::rhs_into_delta(v);
let ret = {
match Ordering::TYPE {
OrderingType::Full => T::Repr::atomic_fetch_add(&self.0, v),
OrderingType::Acquire => T::Repr::atomic_fetch_add_acquire(&self.0, v),
OrderingType::Release => T::Repr::atomic_fetch_add_release(&self.0, v),
OrderingType::Relaxed => T::Repr::atomic_fetch_add_relaxed(&self.0, v),
}
};
unsafe { from_repr(ret) }
}
#[inline(always)]
pub fn fetch_sub<Rhs, Ordering: ordering::Ordering>(&self, v: Rhs, _: Ordering) -> T
where
T: AtomicAdd<Rhs>,
{
let v = T::rhs_into_delta(v);
let ret = {
match Ordering::TYPE {
OrderingType::Full => T::Repr::atomic_fetch_sub(&self.0, v),
OrderingType::Acquire => T::Repr::atomic_fetch_sub_acquire(&self.0, v),
OrderingType::Release => T::Repr::atomic_fetch_sub_release(&self.0, v),
OrderingType::Relaxed => T::Repr::atomic_fetch_sub_relaxed(&self.0, v),
}
};
unsafe { from_repr(ret) }
}
}
#[cfg(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64))]
#[repr(C)]
#[derive(Clone, Copy)]
struct Flag {
bool_field: bool,
}
#[cfg(not(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64)))]
#[repr(C, align(4))]
#[derive(Clone, Copy)]
struct Flag {
#[cfg(target_endian = "big")]
padding: [u8; 3],
bool_field: bool,
#[cfg(target_endian = "little")]
padding: [u8; 3],
}
impl Flag {
#[inline(always)]
const fn new(b: bool) -> Self {
Self {
bool_field: b,
#[cfg(not(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64)))]
padding: [0; 3],
}
}
}
unsafe impl AtomicType for Flag {
#[cfg(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64))]
type Repr = i8;
#[cfg(not(any(CONFIG_X86_64, CONFIG_UML, CONFIG_ARM, CONFIG_ARM64)))]
type Repr = i32;
}
pub struct AtomicFlag(Atomic<Flag>);
impl AtomicFlag {
#[inline(always)]
pub const fn new(b: bool) -> Self {
Self(Atomic::new(Flag::new(b)))
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut bool {
&mut self.0.get_mut().bool_field
}
#[inline(always)]
pub fn load<Ordering: ordering::AcquireOrRelaxed>(&self, o: Ordering) -> bool {
self.0.load(o).bool_field
}
#[inline(always)]
pub fn store<Ordering: ordering::ReleaseOrRelaxed>(&self, v: bool, o: Ordering) {
self.0.store(Flag::new(v), o);
}
#[inline(always)]
pub fn xchg<Ordering: ordering::Ordering>(&self, new: bool, o: Ordering) -> bool {
self.0.xchg(Flag::new(new), o).bool_field
}
#[inline(always)]
pub fn cmpxchg<Ordering: ordering::Ordering>(
&self,
old: bool,
new: bool,
o: Ordering,
) -> Result<bool, bool> {
match self.0.cmpxchg(Flag::new(old), Flag::new(new), o) {
Ok(_) => Ok(old),
Err(f) => Err(f.bool_field),
}
}
}
#[doc(alias("READ_ONCE", "smp_load_acquire"))]
#[inline(always)]
pub unsafe fn atomic_load<T: AtomicType, Ordering: ordering::AcquireOrRelaxed>(
ptr: *mut T,
o: Ordering,
) -> T
where
T::Repr: AtomicBasicOps,
{
unsafe { Atomic::from_ptr(ptr) }.load(o)
}
#[doc(alias("WRITE_ONCE", "smp_store_release"))]
#[inline(always)]
pub unsafe fn atomic_store<T: AtomicType, Ordering: ordering::ReleaseOrRelaxed>(
ptr: *mut T,
v: T,
o: Ordering,
) where
T::Repr: AtomicBasicOps,
{
unsafe { Atomic::from_ptr(ptr) }.store(v, o);
}
#[inline(always)]
pub unsafe fn xchg<T: AtomicType, Ordering: ordering::Ordering>(
ptr: *mut T,
new: T,
o: Ordering,
) -> T
where
T::Repr: AtomicExchangeOps,
{
unsafe { Atomic::from_ptr(ptr) }.xchg(new, o)
}
#[doc(alias("try_cmpxchg"))]
#[inline(always)]
pub unsafe fn cmpxchg<T: AtomicType, Ordering: ordering::Ordering>(
ptr: *mut T,
old: T,
new: T,
o: Ordering,
) -> Result<T, T>
where
T::Repr: AtomicExchangeOps,
{
unsafe { Atomic::from_ptr(ptr) }.cmpxchg(old, new, o)
}