#![no_std]
mod generated;
mod unordered_copy;
use core::{
cell::UnsafeCell, hint::assert_unchecked, marker::PhantomData, mem::MaybeUninit, ptr::NonNull,
};
use generated::*;
use unordered_copy::*;
use crate::private::Sealed;
pub trait RacyStorage: Sealed {}
impl RacyStorage for u8 {}
impl RacyStorage for u16 {}
impl RacyStorage for u32 {}
impl RacyStorage for u64 {}
impl RacyStorage for usize {}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Ordering {
Unordered,
SeqCst,
}
#[inline(always)]
pub fn fence() {
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
}
#[inline(always)]
pub fn atomic_pause() {
core::hint::spin_loop();
}
pub struct RacyMemory<T: Sealed> {
ptr: RacyPtr<T>,
len: usize,
}
unsafe impl<T: Sealed> Send for RacyMemory<T> {}
unsafe impl<T: Sealed> Sync for RacyMemory<T> {}
impl<T: Sealed> RacyMemory<T> {
#[inline]
#[must_use]
pub unsafe fn enter(ptr: NonNull<u8>, len: usize) -> RacyMemory<u8> {
let mut ptr = ptr.as_ptr();
unsafe {
core::arch::asm!(
"/* Magic spell: let {} no longer be memory in Rust's eyes! */",
inlateout(reg) ptr,
options(nostack, preserves_flags)
)
}
let ptr = RacyPtr::from_ptr(unsafe { NonNull::new_unchecked(ptr.cast()) });
RacyMemory::from_raw_parts(ptr, len)
}
pub unsafe fn enter_ptr(ptr: NonNull<T>) -> Self {
let RacyMemory { ptr, .. } = unsafe { Self::enter(ptr.cast(), size_of::<T>()) };
Self::from_raw_parts(ptr.cast(), 1)
}
pub unsafe fn enter_slice(ptr: NonNull<[T]>) -> Self {
let len = ptr.len();
let RacyMemory { ptr, .. } = unsafe { Self::enter(ptr.cast(), size_of::<T>() * len) };
Self::from_raw_parts(ptr.cast(), len)
}
#[inline]
#[must_use]
pub unsafe fn exit(self) -> (NonNull<T>, usize) {
let mut ptr = self.ptr.as_ptr().as_ptr();
unsafe {
core::arch::asm!(
"/* Magic spell: let {} be memory in Rust's eyes! */",
inlateout(reg) ptr,
options(nostack, preserves_flags)
)
}
(unsafe { NonNull::new_unchecked(ptr.cast()) }, self.len)
}
#[inline(always)]
pub const fn as_slice(&self) -> RacySlice<'_, T> {
unsafe { RacySlice::from_raw_parts(self.ptr, self.len) }
}
#[inline(always)]
pub const fn into_raw_parts(self) -> (RacyPtr<T>, usize) {
(self.ptr, self.len)
}
pub const fn from_raw_parts(ptr: RacyPtr<T>, len: usize) -> Self {
Self { ptr, len }
}
}
mod private {
pub trait Sealed: Copy + Eq + Send + Sync + core::fmt::Display {}
impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl Sealed for usize {}
}
#[derive(Clone, Copy)]
pub struct RacySlice<'a, T: Sealed> {
ptr: RacyPtr<T>,
len: usize,
__marker: PhantomData<&'a UnsafeCell<T>>,
}
unsafe impl<T: Sealed> Send for RacySlice<'_, T> {}
unsafe impl<T: Sealed> Sync for RacySlice<'_, T> {}
impl<'a, T: Sealed> RacySlice<'a, T> {
#[inline(always)]
pub const fn into_raw_parts(self) -> (RacyPtr<T>, usize) {
(self.ptr, self.len)
}
#[inline(always)]
pub const unsafe fn from_raw_parts(ptr: RacyPtr<T>, len: usize) -> Self {
Self {
ptr,
len,
__marker: PhantomData,
}
}
const fn as_ptr(&self) -> RacyPtr<T> {
self.ptr
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.len
}
const fn byte_length(&self) -> usize {
unsafe {
assert_unchecked(self.len().checked_mul(size_of::<T>()).is_some());
self.len.unchecked_mul(size_of::<T>())
}
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn align_to<U: Sealed>(self) -> (RacySlice<'a, u8>, RacySlice<'a, U>, RacySlice<'a, u8>) {
let byte_length = self.byte_length();
let head_ptr = self.ptr.as_ptr();
let head_length = head_ptr.align_offset(align_of::<U>()).min(byte_length);
let body_ptr = unsafe { head_ptr.byte_add(head_length) };
let remaining_byte_length = byte_length - head_length;
let body_length = remaining_byte_length / size_of::<U>();
let tail_length = remaining_byte_length % size_of::<U>();
debug_assert_eq!(
head_length + body_length * size_of::<U>() + tail_length,
byte_length,
"align_to produced a collection of slices with different total byte length than original"
);
let tail_ptr = unsafe { body_ptr.byte_add(body_length * size_of::<U>()) };
if const { align_of::<U>() <= align_of::<T>() } {
unsafe {
assert_unchecked(
head_length == 0
&& tail_length == 0
&& body_length == byte_length / size_of::<U>(),
);
};
}
(
RacySlice {
ptr: RacyPtr::from_ptr(head_ptr),
len: head_length,
__marker: PhantomData,
},
RacySlice {
ptr: RacyPtr::from_ptr(body_ptr),
len: body_length,
__marker: PhantomData,
},
RacySlice {
ptr: RacyPtr::from_ptr(tail_ptr),
len: tail_length,
__marker: PhantomData,
},
)
}
#[inline(always)]
pub const fn to_bytes(self) -> RacySlice<'a, u8> {
unsafe { RacySlice::from_raw_parts(self.as_ptr().cast(), self.byte_length()) }
}
#[inline]
pub fn slice_from(&self, offset: usize) -> Self {
let ptr = RacyPtr::from_ptr(unsafe {
self.ptr
.as_ptr()
.byte_add(offset.min(self.len) * size_of::<T>())
});
let len = self.len.saturating_sub(offset);
unsafe { Self::from_raw_parts(ptr, len) }
}
#[inline]
pub fn slice_to(&self, offset: usize) -> Self {
let ptr = self.ptr;
let len = self.len.min(offset);
unsafe { Self::from_raw_parts(ptr, len) }
}
#[inline]
pub fn slice(&self, start: usize, end: usize) -> Self {
let start = start.min(self.len);
let end = end.min(self.len);
let len = end.saturating_sub(start);
let ptr = RacyPtr::from_ptr(unsafe { self.ptr.as_ptr().byte_add(start * size_of::<T>()) });
unsafe { Self::from_raw_parts(ptr, len) }
}
#[inline]
pub fn load_u8(&self) -> Option<u8> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.is_empty() {
return None;
}
Some(atomic_load_8_unsynchronized(self.ptr.as_ptr()))
}
#[inline]
pub fn load_u16(&self) -> Option<u16> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 2 {
return None;
}
if const { align_of::<T>() >= align_of::<u16>() } || UNALIGNED_ACCESS_IS_OK {
return Some(atomic_load_16_unsynchronized(self.ptr.as_ptr()));
}
let mut scratch = MaybeUninit::<u16>::uninit();
let dst = unsafe { NonNull::new_unchecked(scratch.as_mut_ptr().cast::<()>()) };
unsafe { unordered_memcpy_down_unsynchronized(self.ptr.as_ptr(), dst, 2) };
Some(unsafe { scratch.assume_init() })
}
#[inline]
pub fn load_u32(&self) -> Option<u32> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 4 {
return None;
}
if const { align_of::<T>() >= align_of::<u32>() } || UNALIGNED_ACCESS_IS_OK {
return Some(atomic_load_32_unsynchronized(self.ptr.as_ptr()));
}
let mut scratch = MaybeUninit::<u32>::uninit();
let dst = unsafe { NonNull::new_unchecked(scratch.as_mut_ptr().cast::<()>()) };
unsafe { unordered_memcpy_down_unsynchronized(self.ptr.as_ptr(), dst, 4) };
Some(unsafe { scratch.assume_init() })
}
#[inline]
pub fn load_u64(&self) -> Option<u64> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 8 {
return None;
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
if const { align_of::<T>() >= align_of::<u64>() } || UNALIGNED_ACCESS_IS_OK {
return Some(atomic_load_64_unsynchronized(self.ptr.as_ptr()));
}
let mut scratch = MaybeUninit::<u64>::uninit();
let dst = unsafe { NonNull::new_unchecked(scratch.as_mut_ptr().cast::<()>()) };
unsafe { unordered_memcpy_down_unsynchronized(self.ptr.as_ptr(), dst, 8) };
Some(unsafe { scratch.assume_init() })
}
#[inline(always)]
pub fn load_usize(&self) -> Option<usize> {
#[cfg(target_pointer_width = "16")]
{
self.load_u16().map(|u| u as usize)
}
#[cfg(target_pointer_width = "32")]
{
self.load_u32().map(|u| u as usize)
}
#[cfg(target_pointer_width = "64")]
{
self.load_u64().map(|u| u as usize)
}
}
#[inline]
pub fn store_u8(&self, val: u8) -> Option<()> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.is_empty() {
return None;
}
atomic_store_8_unsynchronized(self.ptr.as_ptr(), val);
Some(())
}
#[inline]
pub fn store_u16(&self, val: u16) -> Option<()> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 2 {
return None;
}
if const { align_of::<T>() >= align_of::<u16>() } || UNALIGNED_ACCESS_IS_OK {
return {
atomic_store_16_unsynchronized(self.ptr.as_ptr(), val);
Some(())
};
}
let src = unsafe { NonNull::new_unchecked(&val as *const _ as *mut ()) };
unsafe { unordered_memcpy_down_unsynchronized(src, self.ptr.as_ptr(), 2) };
Some(())
}
#[inline]
pub fn store_u32(&self, val: u32) -> Option<()> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 4 {
return None;
}
if const { align_of::<T>() >= align_of::<u32>() } || UNALIGNED_ACCESS_IS_OK {
return {
atomic_store_32_unsynchronized(self.ptr.as_ptr(), val);
Some(())
};
}
let src = unsafe { NonNull::new_unchecked(&val as *const _ as *mut ()) };
unsafe { unordered_memcpy_down_unsynchronized(src, self.ptr.as_ptr(), 4) };
Some(())
}
#[inline]
pub fn store_u64(&self, val: u64) -> Option<()> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < 8 {
return None;
}
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
if const { align_of::<T>() >= align_of::<u64>() } || UNALIGNED_ACCESS_IS_OK {
return {
atomic_store_64_unsynchronized(self.ptr.as_ptr(), val);
Some(())
};
}
let src = unsafe { NonNull::new_unchecked(&val as *const _ as *mut ()) };
unsafe { unordered_memcpy_down_unsynchronized(src, self.ptr.as_ptr(), 8) };
Some(())
}
#[inline]
pub fn store_usize(&self, val: usize) -> Option<()> {
#[cfg(target_pointer_width = "16")]
{
self.store_u16(val as u16)
}
#[cfg(target_pointer_width = "32")]
{
self.store_u32(val as u32)
}
#[cfg(target_pointer_width = "64")]
{
self.store_u64(val as u64)
}
}
#[inline]
pub fn as_u8(&self) -> Option<RacyU8<'a>> {
if self.is_empty() {
None
} else {
Some(RacyU8::from_ptr(self.ptr.as_ptr()))
}
}
#[inline]
pub fn as_u16(&self) -> Option<RacyU16<'a>> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < size_of::<u16>() || !self.ptr.as_ptr().cast::<u16>().is_aligned() {
None
} else {
Some(RacyU16::from_ptr(self.ptr.as_ptr()))
}
}
#[inline]
pub fn as_u32(&self) -> Option<RacyU32<'a>> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < size_of::<u32>() || !self.ptr.as_ptr().cast::<u32>().is_aligned() {
None
} else {
Some(RacyU32::from_ptr(self.ptr.as_ptr()))
}
}
#[inline]
pub fn as_u64(&self) -> Option<RacyU64<'a>> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < size_of::<u64>() || !self.ptr.as_ptr().cast::<u64>().is_aligned() {
None
} else {
Some(RacyU64::from_ptr(self.ptr.as_ptr()))
}
}
#[inline]
pub fn as_usize(&self) -> Option<RacyUsize<'a>> {
unsafe { assert_unchecked(self.ptr.as_ptr().cast::<T>().is_aligned()) };
if self.byte_length() < size_of::<usize>()
|| !self.ptr.as_ptr().cast::<usize>().is_aligned()
{
None
} else {
Some(RacyUsize::from_ptr(self.ptr.as_ptr()))
}
}
pub fn copy_from_slice(&self, src: &[T]) {
if self.len() != src.len() {
len_mismatch_fail();
}
let count = self.len();
let src = unsafe { NonNull::new_unchecked(src.as_ptr().cast_mut()) };
let dst: NonNull<T> = self.as_ptr().as_ptr().cast();
unsafe { unordered_copy_nonoverlapping(src, dst, count) };
}
pub fn copy_into_slice(&self, dst: &mut [T]) {
if self.len() != dst.len() {
len_mismatch_fail();
}
let count = self.len();
let src: NonNull<T> = self.as_ptr().as_ptr().cast();
let dst = unsafe { NonNull::new_unchecked(dst.as_ptr().cast_mut()) };
unsafe { unordered_copy_nonoverlapping(src, dst, count) };
}
pub fn copy_from_racy_slice(&self, other: &Self) {
if self.len() != other.len() {
len_mismatch_fail();
}
let count = self.len();
if unsafe {
self.ptr.as_ptr() <= other.ptr.as_ptr()
&& other.ptr.as_ptr() < self.ptr.as_ptr().byte_add(self.byte_length())
|| other.ptr.as_ptr() <= self.ptr.as_ptr()
&& self.ptr.as_ptr() < other.ptr.as_ptr().byte_add(other.byte_length())
} {
unsafe { unordered_copy(other.as_ptr(), self.as_ptr(), count) };
} else {
let dst: NonNull<T> = self.as_ptr().as_ptr().cast();
let src: NonNull<T> = other.as_ptr().as_ptr().cast();
unsafe { unordered_copy_nonoverlapping(src, dst, count) };
}
}
}
#[track_caller]
#[cold]
const fn len_mismatch_fail() -> ! {
panic!("copy_from_slice: source slice length does not match destination slice length")
}
impl<'a> RacySlice<'a, u8> {
pub fn get(&self, index: usize) -> Option<RacyU8<'a>> {
self.slice_from(index).as_u8()
}
}
impl<'a> RacySlice<'a, u16> {
pub fn get(&self, index: usize) -> Option<RacyU16<'a>> {
self.slice_from(index).as_u16()
}
}
impl<'a> RacySlice<'a, u32> {
pub fn get(&self, index: usize) -> Option<RacyU32<'a>> {
self.slice_from(index).as_u32()
}
}
impl<'a> RacySlice<'a, u64> {
pub fn get(&self, index: usize) -> Option<RacyU64<'a>> {
self.slice_from(index).as_u64()
}
}
impl<'a> RacySlice<'a, usize> {
pub fn get(&self, index: usize) -> Option<RacyUsize<'a>> {
self.slice_from(index).as_usize()
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct RacyPtr<T: Sealed>(NonNull<()>, PhantomData<NonNull<UnsafeCell<T>>>);
unsafe impl<T: Sealed> Send for RacyPtr<T> {}
unsafe impl<T: Sealed> Sync for RacyPtr<T> {}
impl<T: Sealed> RacyPtr<T> {
#[inline(always)]
#[must_use]
pub const fn dangling() -> RacyPtr<T> {
Self::from_ptr(NonNull::<T>::dangling().cast())
}
const fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
pub const fn as_ptr(self) -> NonNull<()> {
self.0
}
pub const fn cast<U: Sealed>(self) -> RacyPtr<U> {
RacyPtr(self.0.cast(), PhantomData)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RacyU8<'a>(NonNull<()>, PhantomData<&'a UnsafeCell<u8>>);
unsafe impl Send for RacyU8<'_> {}
unsafe impl Sync for RacyU8<'_> {}
impl RacyU8<'_> {
fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
fn as_ptr(&self) -> NonNull<()> {
self.0
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn compare_exchange(&self, current: u8, new: u8) -> Result<u8, u8> {
let old = atomic_cmp_xchg_8_seq_cst(self.as_ptr(), current, new);
if old == current { Ok(old) } else { Err(old) }
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_add(&self, val: u8) -> u8 {
atomic_add_8_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_and(&self, val: u8) -> u8 {
atomic_and_8_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_or(&self, val: u8) -> u8 {
atomic_or_8_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_xor(&self, val: u8) -> u8 {
atomic_xor_8_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn load(&self, order: Ordering) -> u8 {
if order == Ordering::SeqCst {
atomic_load_8_seq_cst(self.as_ptr())
} else {
atomic_load_8_unsynchronized(self.as_ptr())
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn store(&self, val: u8, order: Ordering) {
if order == Ordering::SeqCst {
atomic_store_8_seq_cst(self.as_ptr(), val)
} else {
atomic_store_8_unsynchronized(self.as_ptr(), val)
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn swap(&self, val: u8) -> u8 {
atomic_exchange_8_seq_cst(self.as_ptr(), val)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RacyU16<'a>(NonNull<()>, PhantomData<&'a UnsafeCell<u16>>);
unsafe impl Send for RacyU16<'_> {}
unsafe impl Sync for RacyU16<'_> {}
impl RacyU16<'_> {
fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
fn as_ptr(&self) -> NonNull<()> {
self.0
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn compare_exchange(&self, current: u16, new: u16) -> Result<u16, u16> {
let old = atomic_cmp_xchg_16_seq_cst(self.as_ptr(), current, new);
if old == current { Ok(old) } else { Err(old) }
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_add(&self, val: u16) -> u16 {
atomic_add_16_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_and(&self, val: u16) -> u16 {
atomic_and_16_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_or(&self, val: u16) -> u16 {
atomic_or_16_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_xor(&self, val: u16) -> u16 {
atomic_xor_16_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn load(&self, order: Ordering) -> u16 {
if order == Ordering::SeqCst {
atomic_load_16_seq_cst(self.as_ptr())
} else {
atomic_load_16_unsynchronized(self.as_ptr())
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn store(&self, val: u16, order: Ordering) {
if order == Ordering::SeqCst {
atomic_store_16_seq_cst(self.as_ptr(), val)
} else {
atomic_store_16_unsynchronized(self.as_ptr(), val)
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn swap(&self, val: u16) -> u16 {
atomic_exchange_16_seq_cst(self.as_ptr(), val)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RacyU32<'a>(NonNull<()>, PhantomData<&'a UnsafeCell<u32>>);
unsafe impl Send for RacyU32<'_> {}
unsafe impl Sync for RacyU32<'_> {}
impl RacyU32<'_> {
fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
fn as_ptr(&self) -> NonNull<()> {
self.0
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn compare_exchange(&self, current: u32, new: u32) -> Result<u32, u32> {
let old = atomic_cmp_xchg_32_seq_cst(self.as_ptr(), current, new);
if old == current { Ok(old) } else { Err(old) }
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_add(&self, val: u32) -> u32 {
atomic_add_32_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_and(&self, val: u32) -> u32 {
atomic_and_32_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_or(&self, val: u32) -> u32 {
atomic_or_32_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn fetch_xor(&self, val: u32) -> u32 {
atomic_xor_32_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn load(&self, order: Ordering) -> u32 {
if order == Ordering::SeqCst {
atomic_load_32_seq_cst(self.as_ptr())
} else {
atomic_load_32_unsynchronized(self.as_ptr())
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn store(&self, val: u32, order: Ordering) {
if order == Ordering::SeqCst {
atomic_store_32_seq_cst(self.as_ptr(), val)
} else {
atomic_store_32_unsynchronized(self.as_ptr(), val)
}
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn swap(&self, val: u32) -> u32 {
atomic_exchange_32_seq_cst(self.as_ptr(), val)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RacyU64<'a>(NonNull<()>, PhantomData<&'a UnsafeCell<u64>>);
unsafe impl Send for RacyU64<'_> {}
unsafe impl Sync for RacyU64<'_> {}
impl RacyU64<'_> {
fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
fn as_ptr(&self) -> NonNull<()> {
self.0
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn compare_exchange(&self, current: u64, new: u64) -> Result<u64, u64> {
let old = atomic_cmp_xchg_64_seq_cst(self.as_ptr(), current, new);
if old == current { Ok(old) } else { Err(old) }
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_add(&self, val: u64) -> u64 {
atomic_add_64_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_and(&self, val: u64) -> u64 {
atomic_and_64_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_or(&self, val: u64) -> u64 {
atomic_or_64_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_xor(&self, val: u64) -> u64 {
atomic_xor_64_seq_cst(self.as_ptr(), val)
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn load(&self, order: Ordering) -> u64 {
if order == Ordering::SeqCst {
atomic_load_64_seq_cst(self.as_ptr())
} else {
atomic_load_64_unsynchronized(self.as_ptr())
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn store(&self, val: u64, order: Ordering) {
if order == Ordering::SeqCst {
atomic_store_64_seq_cst(self.as_ptr(), val)
} else {
atomic_store_64_unsynchronized(self.as_ptr(), val)
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn swap(&self, val: u64) -> u64 {
atomic_exchange_64_seq_cst(self.as_ptr(), val)
}
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct RacyUsize<'a>(NonNull<()>, PhantomData<&'a UnsafeCell<usize>>);
unsafe impl Send for RacyUsize<'_> {}
unsafe impl Sync for RacyUsize<'_> {}
impl RacyUsize<'_> {
fn from_ptr(ptr: NonNull<()>) -> Self {
Self(ptr, PhantomData)
}
fn as_ptr(&self) -> NonNull<()> {
self.0
}
#[inline]
#[cfg(any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm"
))]
pub fn compare_exchange(&self, current: usize, new: usize) -> Result<usize, usize> {
#[cfg(target_pointer_width = "16")]
{
let old =
atomic_cmp_xchg_16_seq_cst(self.as_ptr(), current as u16, new as u16) as usize;
if old == current { Ok(old) } else { Err(old) }
}
#[cfg(target_pointer_width = "32")]
{
let old =
atomic_cmp_xchg_16_seq_cst(self.as_ptr(), current as u32, new as u32) as usize;
if old == current { Ok(old) } else { Err(old) }
}
#[cfg(target_pointer_width = "64")]
{
let old =
atomic_cmp_xchg_64_seq_cst(self.as_ptr(), current as u64, new as u64) as usize;
if old == current { Ok(old) } else { Err(old) }
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_add(&self, val: usize) -> usize {
#[cfg(target_pointer_width = "16")]
{
atomic_add_16_seq_cst(self.as_ptr(), val as u16) as usize
}
#[cfg(target_pointer_width = "32")]
{
atomic_add_32_seq_cst(self.as_ptr(), val as u32) as usize
}
#[cfg(target_pointer_width = "64")]
{
atomic_add_64_seq_cst(self.as_ptr(), val as u64) as usize
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_and(&self, val: usize) -> usize {
#[cfg(target_pointer_width = "16")]
{
atomic_and_16_seq_cst(self.as_ptr(), val as u16) as usize
}
#[cfg(target_pointer_width = "32")]
{
atomic_and_32_seq_cst(self.as_ptr(), val as u32) as usize
}
#[cfg(target_pointer_width = "64")]
{
atomic_and_64_seq_cst(self.as_ptr(), val as u64) as usize
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_or(&self, val: usize) -> usize {
#[cfg(target_pointer_width = "16")]
{
atomic_or_16_seq_cst(self.as_ptr(), val as u16) as usize
}
#[cfg(target_pointer_width = "32")]
{
atomic_or_32_seq_cst(self.as_ptr(), val as u32) as usize
}
#[cfg(target_pointer_width = "64")]
{
atomic_or_64_seq_cst(self.as_ptr(), val as u64) as usize
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn fetch_xor(&self, val: usize) -> usize {
#[cfg(target_pointer_width = "16")]
{
atomic_xor_16_seq_cst(self.as_ptr(), val as u16) as usize
}
#[cfg(target_pointer_width = "32")]
{
atomic_xor_32_seq_cst(self.as_ptr(), val as u32) as usize
}
#[cfg(target_pointer_width = "64")]
{
atomic_xor_64_seq_cst(self.as_ptr(), val as u64) as usize
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn load(&self, order: Ordering) -> usize {
#[cfg(target_pointer_width = "16")]
{
if order == Ordering::SeqCst {
atomic_load_16_seq_cst(self.as_ptr()) as usize
} else {
atomic_load_16_unsynchronized(self.as_ptr()) as usize
}
}
#[cfg(target_pointer_width = "32")]
{
if order == Ordering::SeqCst {
atomic_load_32_seq_cst(self.as_ptr()) as usize
} else {
atomic_load_32_unsynchronized(self.as_ptr()) as usize
}
}
#[cfg(target_pointer_width = "64")]
{
if order == Ordering::SeqCst {
atomic_load_64_seq_cst(self.as_ptr()) as usize
} else {
atomic_load_64_unsynchronized(self.as_ptr()) as usize
}
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn store(&self, val: usize, order: Ordering) {
#[cfg(target_pointer_width = "16")]
{
if order == Ordering::SeqCst {
atomic_store_16_seq_cst(self.as_ptr(), val as u16)
} else {
atomic_store_16_unsynchronized(self.as_ptr(), val as u16)
}
}
#[cfg(target_pointer_width = "32")]
{
if order == Ordering::SeqCst {
atomic_store_32_seq_cst(self.as_ptr(), val as u32)
} else {
atomic_store_32_unsynchronized(self.as_ptr(), val as u32)
}
}
#[cfg(target_pointer_width = "64")]
{
if order == Ordering::SeqCst {
atomic_store_64_seq_cst(self.as_ptr(), val as u64)
} else {
atomic_store_64_unsynchronized(self.as_ptr(), val as u64)
}
}
}
#[inline]
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64",))]
pub fn swap(&self, val: usize) -> usize {
#[cfg(target_pointer_width = "16")]
{
atomic_exchange_16_seq_cst(self.as_ptr(), val as u16) as usize
}
#[cfg(target_pointer_width = "32")]
{
atomic_exchange_32_seq_cst(self.as_ptr(), val as u32) as usize
}
#[cfg(target_pointer_width = "64")]
{
atomic_exchange_64_seq_cst(self.as_ptr(), val as u64) as usize
}
}
}
#[inline]
unsafe fn unordered_copy_nonoverlapping<T: Sealed>(src: NonNull<T>, dst: NonNull<T>, count: usize) {
let count = count * size_of::<T>();
unsafe { unordered_memcpy_down_unsynchronized(src.cast(), dst.cast(), count) };
}
#[inline]
unsafe fn unordered_copy<T: Sealed>(src: RacyPtr<T>, dst: RacyPtr<T>, count: usize) {
let count = count * size_of::<T>();
if dst.as_ptr() <= src.as_ptr() {
unsafe { unordered_memcpy_down_unsynchronized(src.as_ptr(), dst.as_ptr(), count) };
} else {
unsafe { unordered_memcpy_up_unsynchronized(src.as_ptr(), dst.as_ptr(), count) };
}
}