#[cfg(not(portable_atomic_no_asm))]
use core::arch::asm;
#[cfg(any(test, not(feature = "critical-section")))]
use core::cell::UnsafeCell;
use core::sync::atomic::Ordering;
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub fn fence(order: Ordering) {
match order {
Ordering::Relaxed => panic!("there is no such thing as a relaxed fence"),
_ => compiler_fence(order),
}
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub fn compiler_fence(order: Ordering) {
match order {
Ordering::Relaxed => panic!("there is no such thing as a relaxed compiler fence"),
_ => {}
}
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!("", options(nostack, preserves_flags));
#[cfg(portable_atomic_no_asm)]
llvm_asm!("" ::: "memory" : "volatile");
}
}
#[cfg(any(test, not(feature = "critical-section")))]
#[repr(transparent)]
pub(crate) struct AtomicBool {
v: UnsafeCell<u8>,
}
#[cfg(any(test, not(feature = "critical-section")))]
unsafe impl Sync for AtomicBool {}
#[cfg(any(test, not(feature = "critical-section")))]
impl AtomicBool {
#[cfg(test)]
#[inline]
pub(crate) const fn new(v: bool) -> Self {
Self { v: UnsafeCell::new(v as u8) }
}
#[cfg(test)]
#[inline]
pub(crate) fn is_lock_free() -> bool {
Self::is_always_lock_free()
}
#[cfg(test)]
#[inline]
pub(crate) const fn is_always_lock_free() -> bool {
true
}
#[cfg(test)]
#[inline]
pub(crate) fn get_mut(&mut self) -> &mut bool {
unsafe { &mut *(self.v.get() as *mut bool) }
}
#[cfg(test)]
#[inline]
pub(crate) fn into_inner(self) -> bool {
self.v.into_inner() != 0
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn load(&self, order: Ordering) -> bool {
crate::utils::assert_load_ordering(order);
unsafe { u8::atomic_load(self.v.get()) != 0 }
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn store(&self, val: bool, order: Ordering) {
crate::utils::assert_store_ordering(order);
unsafe {
u8::atomic_store(self.v.get(), val as u8);
}
}
#[inline]
pub(crate) fn and(&self, val: bool, _order: Ordering) {
unsafe {
u8::atomic_and(self.v.get(), val as u8);
}
}
#[inline]
pub(crate) fn or(&self, val: bool, _order: Ordering) {
unsafe {
u8::atomic_or(self.v.get(), val as u8);
}
}
#[inline]
pub(crate) fn xor(&self, val: bool, _order: Ordering) {
unsafe {
u8::atomic_xor(self.v.get(), val as u8);
}
}
}
#[cfg(any(test, not(feature = "critical-section")))]
#[repr(transparent)]
pub(crate) struct AtomicPtr<T> {
p: UnsafeCell<*mut T>,
}
#[cfg(any(test, not(feature = "critical-section")))]
unsafe impl<T> Send for AtomicPtr<T> {}
#[cfg(any(test, not(feature = "critical-section")))]
unsafe impl<T> Sync for AtomicPtr<T> {}
#[cfg(any(test, not(feature = "critical-section")))]
impl<T> AtomicPtr<T> {
#[cfg(test)]
#[inline]
pub(crate) const fn new(p: *mut T) -> Self {
Self { p: UnsafeCell::new(p) }
}
#[cfg(test)]
#[inline]
pub(crate) fn is_lock_free() -> bool {
Self::is_always_lock_free()
}
#[cfg(test)]
#[inline]
pub(crate) const fn is_always_lock_free() -> bool {
true
}
#[cfg(test)]
#[inline]
pub(crate) fn get_mut(&mut self) -> &mut *mut T {
unsafe { &mut *self.p.get() }
}
#[cfg(test)]
#[inline]
pub(crate) fn into_inner(self) -> *mut T {
self.p.into_inner()
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn load(&self, order: Ordering) -> *mut T {
crate::utils::assert_load_ordering(order);
unsafe { usize::atomic_load(self.p.get() as *mut usize) as *mut T }
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn store(&self, ptr: *mut T, order: Ordering) {
crate::utils::assert_store_ordering(order);
unsafe {
usize::atomic_store(self.p.get() as *mut usize, ptr as usize);
}
}
}
macro_rules! atomic_int {
($int_type:ident, $atomic_type:ident, $asm_suffix:expr) => {
#[cfg(any(test, not(feature = "critical-section")))]
#[repr(transparent)]
pub(crate) struct $atomic_type {
v: UnsafeCell<$int_type>,
}
#[cfg(any(test, not(feature = "critical-section")))]
unsafe impl Sync for $atomic_type {}
#[cfg(any(test, not(feature = "critical-section")))]
impl $atomic_type {
#[cfg(test)]
#[inline]
pub(crate) const fn new(v: $int_type) -> Self {
Self { v: UnsafeCell::new(v) }
}
#[cfg(test)]
#[inline]
pub(crate) fn is_lock_free() -> bool {
Self::is_always_lock_free()
}
#[cfg(test)]
#[inline]
pub(crate) const fn is_always_lock_free() -> bool {
true
}
#[cfg(test)]
#[inline]
pub(crate) fn get_mut(&mut self) -> &mut $int_type {
unsafe { &mut *self.v.get() }
}
#[cfg(test)]
#[inline]
pub(crate) fn into_inner(self) -> $int_type {
self.v.into_inner()
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn load(&self, order: Ordering) -> $int_type {
crate::utils::assert_load_ordering(order);
unsafe { $int_type::atomic_load(self.v.get()) }
}
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn store(&self, val: $int_type, order: Ordering) {
crate::utils::assert_store_ordering(order);
unsafe {
$int_type::atomic_store(self.v.get(), val);
}
}
#[inline]
pub(crate) fn add(&self, val: $int_type, _order: Ordering) {
unsafe {
$int_type::atomic_add(self.v.get(), val);
}
}
#[inline]
pub(crate) fn sub(&self, val: $int_type, _order: Ordering) {
unsafe {
$int_type::atomic_sub(self.v.get(), val);
}
}
#[inline]
pub(crate) fn and(&self, val: $int_type, _order: Ordering) {
unsafe {
$int_type::atomic_and(self.v.get(), val);
}
}
#[inline]
pub(crate) fn or(&self, val: $int_type, _order: Ordering) {
unsafe {
$int_type::atomic_or(self.v.get(), val);
}
}
#[inline]
pub(crate) fn xor(&self, val: $int_type, _order: Ordering) {
unsafe {
$int_type::atomic_xor(self.v.get(), val);
}
}
#[inline]
pub(crate) fn not(&self, _order: Ordering) {
unsafe {
$int_type::atomic_not(self.v.get());
}
}
}
#[cfg(any(test, not(feature = "critical-section")))]
impl AtomicOperations for $int_type {
#[inline]
unsafe fn atomic_load(src: *const Self) -> Self {
unsafe {
let out;
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("mov", $asm_suffix, " @{src}, {out}"),
src = in(reg) src,
out = lateout(reg) out,
options(nostack, preserves_flags),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("mov", $asm_suffix, " $1, $0")
: "=r"(out) : "*m"(src) : "memory" : "volatile"
);
out
}
}
#[inline]
unsafe fn atomic_store(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("mov", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack, preserves_flags),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("mov", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_add(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("add", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("add", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_sub(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("sub", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("sub", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_and(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("and", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("and", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_or(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("bis", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("bis", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_xor(dst: *mut Self, val: Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("xor", $asm_suffix, " {val}, 0({dst})"),
dst = in(reg) dst,
val = in(reg) val,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("xor", $asm_suffix, " $1, $0")
:: "*m"(dst), "ir"(val) : "memory" : "volatile"
);
}
}
#[inline]
unsafe fn atomic_not(dst: *mut Self) {
unsafe {
#[cfg(not(portable_atomic_no_asm))]
asm!(
concat!("inv", $asm_suffix, " 0({dst})"),
dst = in(reg) dst,
options(nostack),
);
#[cfg(portable_atomic_no_asm)]
llvm_asm!(
concat!("inv", $asm_suffix, " $0")
:: "*m"(dst) : "memory" : "volatile"
);
}
}
}
}
}
atomic_int!(i8, AtomicI8, ".b");
atomic_int!(u8, AtomicU8, ".b");
atomic_int!(i16, AtomicI16, ".w");
atomic_int!(u16, AtomicU16, ".w");
atomic_int!(isize, AtomicIsize, ".w");
atomic_int!(usize, AtomicUsize, ".w");
#[cfg(any(test, not(feature = "critical-section")))]
trait AtomicOperations: Sized {
unsafe fn atomic_load(src: *const Self) -> Self;
unsafe fn atomic_store(dst: *mut Self, val: Self);
unsafe fn atomic_add(dst: *mut Self, val: Self);
unsafe fn atomic_sub(dst: *mut Self, val: Self);
unsafe fn atomic_and(dst: *mut Self, val: Self);
unsafe fn atomic_or(dst: *mut Self, val: Self);
unsafe fn atomic_xor(dst: *mut Self, val: Self);
unsafe fn atomic_not(dst: *mut Self);
}