use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr;
use core::sync::atomic::{
AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicPtr, AtomicU16, AtomicU32, AtomicU64,
AtomicU8, Ordering,
};
mod seal {
pub trait Sealed {}
}
pub unsafe trait AtomicLoadable:
Copy + Send + Sync + 'static + Sized + seal::Sealed
{
type Atomic: Send + Sync + 'static;
fn new_atomic(value: Self) -> Self::Atomic;
fn load_acquire(atomic: &Self::Atomic) -> Self;
fn store_release(atomic: &Self::Atomic, value: Self);
fn swap_acqrel(atomic: &Self::Atomic, value: Self) -> Self;
}
macro_rules! impl_atomic_loadable_int {
($($prim:ty => $atomic:ty),* $(,)?) => {
$(
impl seal::Sealed for $prim {}
unsafe impl AtomicLoadable for $prim {
type Atomic = $atomic;
#[inline]
fn new_atomic(value: Self) -> Self::Atomic { <$atomic>::new(value as _) }
#[inline]
fn load_acquire(a: &Self::Atomic) -> Self { a.load(Ordering::Acquire) as _ }
#[inline]
fn store_release(a: &Self::Atomic, value: Self) { a.store(value as _, Ordering::Release) }
#[inline]
fn swap_acqrel(a: &Self::Atomic, value: Self) -> Self {
a.swap(value as _, Ordering::AcqRel) as _
}
}
)*
};
}
impl_atomic_loadable_int!(
u8 => AtomicU8,
i8 => AtomicI8,
u16 => AtomicU16,
i16 => AtomicI16,
u32 => AtomicU32,
i32 => AtomicI32,
u64 => AtomicU64,
i64 => AtomicI64,
);
#[cfg(target_pointer_width = "64")]
mod size_atomics {
use super::*;
impl seal::Sealed for usize {}
unsafe impl AtomicLoadable for usize {
type Atomic = AtomicU64;
#[inline]
fn new_atomic(value: Self) -> Self::Atomic {
AtomicU64::new(value as u64)
}
#[inline]
fn load_acquire(a: &Self::Atomic) -> Self {
a.load(Ordering::Acquire) as usize
}
#[inline]
fn store_release(a: &Self::Atomic, value: Self) {
a.store(value as u64, Ordering::Release)
}
#[inline]
fn swap_acqrel(a: &Self::Atomic, value: Self) -> Self {
a.swap(value as u64, Ordering::AcqRel) as usize
}
}
impl seal::Sealed for isize {}
unsafe impl AtomicLoadable for isize {
type Atomic = AtomicI64;
#[inline]
fn new_atomic(value: Self) -> Self::Atomic {
AtomicI64::new(value as i64)
}
#[inline]
fn load_acquire(a: &Self::Atomic) -> Self {
a.load(Ordering::Acquire) as isize
}
#[inline]
fn store_release(a: &Self::Atomic, value: Self) {
a.store(value as i64, Ordering::Release)
}
#[inline]
fn swap_acqrel(a: &Self::Atomic, value: Self) -> Self {
a.swap(value as i64, Ordering::AcqRel) as isize
}
}
}
#[cfg(target_pointer_width = "32")]
mod size_atomics {
use super::*;
impl seal::Sealed for usize {}
unsafe impl AtomicLoadable for usize {
type Atomic = AtomicU32;
#[inline]
fn new_atomic(value: Self) -> Self::Atomic {
AtomicU32::new(value as u32)
}
#[inline]
fn load_acquire(a: &Self::Atomic) -> Self {
a.load(Ordering::Acquire) as usize
}
#[inline]
fn store_release(a: &Self::Atomic, value: Self) {
a.store(value as u32, Ordering::Release)
}
#[inline]
fn swap_acqrel(a: &Self::Atomic, value: Self) -> Self {
a.swap(value as u32, Ordering::AcqRel) as usize
}
}
impl seal::Sealed for isize {}
unsafe impl AtomicLoadable for isize {
type Atomic = AtomicI32;
#[inline]
fn new_atomic(value: Self) -> Self::Atomic {
AtomicI32::new(value as i32)
}
#[inline]
fn load_acquire(a: &Self::Atomic) -> Self {
a.load(Ordering::Acquire) as isize
}
#[inline]
fn store_release(a: &Self::Atomic, value: Self) {
a.store(value as i32, Ordering::Release)
}
#[inline]
fn swap_acqrel(a: &Self::Atomic, value: Self) -> Self {
a.swap(value as i32, Ordering::AcqRel) as isize
}
}
}
pub unsafe trait OptimisticSlot: Default + Send + Sync + Sized {
type Value;
unsafe fn try_load(&self) -> Option<Self::Value>;
#[inline]
unsafe fn try_load_raw_ptr(this: *const Self) -> Option<Self::Value> {
unsafe { (*this).try_load() }
}
unsafe fn try_load_into_raw_ptr(
this: *const Self,
buf: &mut MaybeUninit<Self::Value>,
) -> Option<&Self::Value>;
#[inline]
unsafe fn store_into_empty_raw_ptr(this: *const Self, value: Self::Value) {
unsafe { (*this).store_into_empty(value) }
}
#[inline]
unsafe fn swap_init_raw_ptr(this: *const Self, value: Self::Value) -> Self::Displaced {
unsafe { (*this).swap_init(value) }
}
#[inline]
unsafe fn take_init_raw_ptr(this: *const Self) -> Self::Displaced {
unsafe { (*this).take_init() }
}
#[inline]
unsafe fn move_init_to_empty_raw_ptr(src: *const Self, dst: *const Self) {
unsafe { (*src).move_init_to_empty(&*dst) }
}
type Displaced: Send + 'static;
unsafe fn load(&self) -> Self::Value;
unsafe fn store_into_empty(&self, value: Self::Value);
unsafe fn swap_init(&self, value: Self::Value) -> Self::Displaced;
unsafe fn take_init(&self) -> Self::Displaced;
unsafe fn move_init_to_empty(&self, dst: &Self);
unsafe fn drop_in_place(&mut self, was_init: bool);
unsafe fn load_into<'a>(&'a self, buf: &'a mut MaybeUninit<Self::Value>) -> &'a Self::Value;
}
#[repr(transparent)]
pub struct InlineSlot<T: AtomicLoadable> {
inner: T::Atomic,
}
impl<T: AtomicLoadable + Default> Default for InlineSlot<T> {
#[inline]
fn default() -> Self {
Self {
inner: T::new_atomic(T::default()),
}
}
}
unsafe impl<T: AtomicLoadable + Default> OptimisticSlot for InlineSlot<T> {
type Value = T;
type Displaced = T;
#[inline]
unsafe fn load(&self) -> T {
T::load_acquire(&self.inner)
}
#[inline]
unsafe fn try_load(&self) -> Option<T> {
Some(T::load_acquire(&self.inner))
}
#[inline]
unsafe fn try_load_raw_ptr(this: *const Self) -> Option<T> {
let atomic_ptr: *const T::Atomic = unsafe { ptr::addr_of!((*this).inner) };
Some(T::load_acquire(unsafe { &*atomic_ptr }))
}
#[inline]
unsafe fn try_load_into_raw_ptr(this: *const Self, buf: &mut MaybeUninit<T>) -> Option<&T> {
let atomic_ptr: *const T::Atomic = unsafe { ptr::addr_of!((*this).inner) };
let value = T::load_acquire(unsafe { &*atomic_ptr });
buf.write(value);
Some(unsafe { buf.assume_init_ref() })
}
#[inline]
unsafe fn store_into_empty_raw_ptr(this: *const Self, value: T) {
let atomic_ptr: *const T::Atomic = unsafe { ptr::addr_of!((*this).inner) };
T::store_release(unsafe { &*atomic_ptr }, value)
}
#[inline]
unsafe fn swap_init_raw_ptr(this: *const Self, value: T) -> T {
let atomic_ptr: *const T::Atomic = unsafe { ptr::addr_of!((*this).inner) };
T::swap_acqrel(unsafe { &*atomic_ptr }, value)
}
#[inline]
unsafe fn take_init_raw_ptr(this: *const Self) -> T {
let atomic_ptr: *const T::Atomic = unsafe { ptr::addr_of!((*this).inner) };
T::swap_acqrel(unsafe { &*atomic_ptr }, T::default())
}
#[inline]
unsafe fn move_init_to_empty_raw_ptr(src: *const Self, dst: *const Self) {
let src_atomic: *const T::Atomic = unsafe { ptr::addr_of!((*src).inner) };
let dst_atomic: *const T::Atomic = unsafe { ptr::addr_of!((*dst).inner) };
let bits = T::load_acquire(unsafe { &*src_atomic });
T::store_release(unsafe { &*dst_atomic }, bits);
T::store_release(unsafe { &*src_atomic }, T::default());
}
#[inline]
unsafe fn store_into_empty(&self, value: T) {
T::store_release(&self.inner, value)
}
#[inline]
unsafe fn swap_init(&self, value: T) -> T {
T::swap_acqrel(&self.inner, value)
}
#[inline]
unsafe fn take_init(&self) -> T {
T::swap_acqrel(&self.inner, T::default())
}
#[inline]
unsafe fn move_init_to_empty(&self, dst: &Self) {
let bits = T::load_acquire(&self.inner);
T::store_release(&dst.inner, bits);
T::store_release(&self.inner, T::default());
}
#[inline]
unsafe fn drop_in_place(&mut self, _was_init: bool) {
}
#[inline]
unsafe fn load_into<'a>(&'a self, buf: &'a mut MaybeUninit<T>) -> &'a T {
let v = T::load_acquire(&self.inner);
buf.write(v)
}
}
pub struct BoxedSlot<T> {
inner: AtomicPtr<T>,
_marker: PhantomData<Box<T>>,
}
impl<T> Default for BoxedSlot<T> {
#[inline]
fn default() -> Self {
Self {
inner: AtomicPtr::new(ptr::null_mut()),
_marker: PhantomData,
}
}
}
unsafe impl<T: Send> Send for BoxedSlot<T> {}
unsafe impl<T: Sync> Sync for BoxedSlot<T> {}
unsafe impl<T: Send + Sync + Clone + 'static> OptimisticSlot for BoxedSlot<T> {
type Value = T;
type Displaced = Box<T>;
#[inline]
unsafe fn load(&self) -> T {
let raw = self.inner.load(Ordering::Acquire);
unsafe { (*raw).clone() }
}
#[inline]
unsafe fn try_load(&self) -> Option<T> {
let raw = self.inner.load(Ordering::Acquire);
if raw.is_null() {
return None;
}
Some(unsafe { (*raw).clone() })
}
#[inline]
unsafe fn try_load_raw_ptr(this: *const Self) -> Option<T> {
let atomic_ptr: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*this).inner) };
let raw = unsafe { (*atomic_ptr).load(Ordering::Acquire) };
if raw.is_null() {
return None;
}
Some(unsafe { (*raw).clone() })
}
#[inline]
unsafe fn try_load_into_raw_ptr(this: *const Self, _buf: &mut MaybeUninit<T>) -> Option<&T> {
let atomic_ptr: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*this).inner) };
let raw = unsafe { (*atomic_ptr).load(Ordering::Acquire) };
if raw.is_null() {
return None;
}
Some(unsafe { &*raw })
}
#[inline]
unsafe fn store_into_empty_raw_ptr(this: *const Self, value: T) {
let atomic_ptr: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*this).inner) };
let raw = Box::into_raw(Box::new(value));
debug_assert!(unsafe { (*atomic_ptr).load(Ordering::Relaxed).is_null() });
unsafe { (*atomic_ptr).store(raw, Ordering::Release) };
}
#[inline]
unsafe fn swap_init_raw_ptr(this: *const Self, value: T) -> Box<T> {
let atomic_ptr: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*this).inner) };
let raw_new = Box::into_raw(Box::new(value));
let raw_old = unsafe { (*atomic_ptr).swap(raw_new, Ordering::AcqRel) };
debug_assert!(!raw_old.is_null());
unsafe { Box::from_raw(raw_old) }
}
#[inline]
unsafe fn take_init_raw_ptr(this: *const Self) -> Box<T> {
let atomic_ptr: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*this).inner) };
let raw_old = unsafe { (*atomic_ptr).swap(ptr::null_mut(), Ordering::AcqRel) };
debug_assert!(!raw_old.is_null());
unsafe { Box::from_raw(raw_old) }
}
#[inline]
unsafe fn move_init_to_empty_raw_ptr(src: *const Self, dst: *const Self) {
let src_atomic: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*src).inner) };
let dst_atomic: *const AtomicPtr<T> = unsafe { ptr::addr_of!((*dst).inner) };
let raw = unsafe { (*src_atomic).swap(ptr::null_mut(), Ordering::AcqRel) };
debug_assert!(!raw.is_null());
debug_assert!(unsafe { (*dst_atomic).load(Ordering::Relaxed).is_null() });
unsafe { (*dst_atomic).store(raw, Ordering::Release) };
}
#[inline]
unsafe fn store_into_empty(&self, value: T) {
let raw = Box::into_raw(Box::new(value));
debug_assert!(self.inner.load(Ordering::Relaxed).is_null());
self.inner.store(raw, Ordering::Release);
}
#[inline]
unsafe fn swap_init(&self, value: T) -> Box<T> {
let raw_new = Box::into_raw(Box::new(value));
let raw_old = self.inner.swap(raw_new, Ordering::AcqRel);
debug_assert!(!raw_old.is_null());
unsafe { Box::from_raw(raw_old) }
}
#[inline]
unsafe fn take_init(&self) -> Box<T> {
let raw_old = self.inner.swap(ptr::null_mut(), Ordering::AcqRel);
debug_assert!(!raw_old.is_null());
unsafe { Box::from_raw(raw_old) }
}
#[inline]
unsafe fn move_init_to_empty(&self, dst: &Self) {
let raw = self.inner.swap(ptr::null_mut(), Ordering::AcqRel);
debug_assert!(!raw.is_null());
debug_assert!(dst.inner.load(Ordering::Relaxed).is_null());
dst.inner.store(raw, Ordering::Release);
}
#[inline]
unsafe fn drop_in_place(&mut self, was_init: bool) {
if was_init {
let raw = *self.inner.get_mut();
debug_assert!(!raw.is_null());
drop(unsafe { Box::from_raw(raw) });
*self.inner.get_mut() = ptr::null_mut();
} else {
debug_assert!(self.inner.load(Ordering::Relaxed).is_null());
}
}
#[inline]
unsafe fn load_into<'a>(&'a self, _buf: &'a mut MaybeUninit<T>) -> &'a T {
let raw = self.inner.load(Ordering::Acquire);
unsafe { &*raw }
}
}
#[derive(Default)]
pub struct UnitSlot;
unsafe impl OptimisticSlot for UnitSlot {
type Value = ();
type Displaced = ();
#[inline]
unsafe fn load(&self) {}
#[inline]
unsafe fn store_into_empty(&self, _value: ()) {}
#[inline]
unsafe fn swap_init(&self, _value: ()) {}
#[inline]
unsafe fn take_init(&self) {}
#[inline]
unsafe fn move_init_to_empty(&self, _dst: &Self) {}
#[inline]
unsafe fn drop_in_place(&mut self, _was_init: bool) {}
#[inline]
unsafe fn load_into<'a>(&'a self, buf: &'a mut MaybeUninit<()>) -> &'a () {
buf.write(())
}
#[inline]
unsafe fn try_load(&self) -> Option<()> {
Some(())
}
#[inline]
unsafe fn try_load_into_raw_ptr(_this: *const Self, buf: &mut MaybeUninit<()>) -> Option<&()> {
Some(buf.write(()))
}
}
#[repr(transparent)]
pub struct SlotArray<S, const N: usize> {
slots: core::cell::UnsafeCell<[S; N]>,
}
unsafe impl<S: Send, const N: usize> Send for SlotArray<S, N> {}
unsafe impl<S: Sync, const N: usize> Sync for SlotArray<S, N> {}
impl<S: Default, const N: usize> Default for SlotArray<S, N> {
fn default() -> Self {
Self {
slots: core::cell::UnsafeCell::new(core::array::from_fn(|_| S::default())),
}
}
}
impl<S, const N: usize> SlotArray<S, N> {
#[inline]
pub fn new() -> Self
where
S: Default,
{
Self::default()
}
#[inline]
pub fn slot(&self, pos: usize) -> &S {
unsafe { &(*self.slots.get())[pos] }
}
}
impl<S, const N: usize> SlotArray<S, N> {
#[inline]
pub unsafe fn slots_ptr(this: *const Self) -> *const S {
let cell_ptr: *const core::cell::UnsafeCell<[S; N]> =
unsafe { ptr::addr_of!((*this).slots) };
core::cell::UnsafeCell::raw_get(cell_ptr) as *const S
}
}
impl<S, const N: usize> SlotArray<S, N>
where
S: OptimisticSlot,
{
#[inline]
pub unsafe fn load(&self, pos: usize) -> S::Value {
debug_assert!(pos < N);
unsafe { (*self.slots.get()).get_unchecked(pos).load() }
}
#[inline]
pub unsafe fn load_raw(this: *const Self, pos: usize) -> S::Value {
debug_assert!(pos < N);
let slot = unsafe { &*Self::slots_ptr(this).add(pos) };
unsafe { slot.load() }
}
#[inline]
pub unsafe fn try_load_raw(this: *const Self, pos: usize) -> Option<S::Value> {
debug_assert!(pos < N);
let slot_ptr = unsafe { Self::slots_ptr(this).add(pos) };
unsafe { S::try_load_raw_ptr(slot_ptr) }
}
#[inline]
pub unsafe fn try_load_into_raw(
this: *const Self,
pos: usize,
buf: &mut MaybeUninit<S::Value>,
) -> Option<&S::Value> {
debug_assert!(pos < N);
let slot_ptr = unsafe { Self::slots_ptr(this).add(pos) };
unsafe { S::try_load_into_raw_ptr(slot_ptr, buf) }
}
#[inline]
pub unsafe fn shift_insert_raw(this: *const Self, len: usize, pos: usize, value: S::Value) {
debug_assert!(pos <= len);
debug_assert!(len < N);
let base = unsafe { Self::slots_ptr(this) };
for i in (pos..len).rev() {
let src_ptr = unsafe { base.add(i) };
let dst_ptr = unsafe { base.add(i + 1) };
unsafe { S::move_init_to_empty_raw_ptr(src_ptr, dst_ptr) };
}
let slot_ptr = unsafe { base.add(pos) };
unsafe { S::store_into_empty_raw_ptr(slot_ptr, value) };
}
#[inline]
pub unsafe fn shift_remove_raw(this: *const Self, len: usize, pos: usize) -> S::Displaced {
debug_assert!(pos < len);
debug_assert!(len <= N);
let base = unsafe { Self::slots_ptr(this) };
let removed = unsafe { S::take_init_raw_ptr(base.add(pos)) };
for i in pos..(len - 1) {
let src_ptr = unsafe { base.add(i + 1) };
let dst_ptr = unsafe { base.add(i) };
unsafe { S::move_init_to_empty_raw_ptr(src_ptr, dst_ptr) };
}
removed
}
#[inline]
pub unsafe fn swap_init_raw(this: *const Self, pos: usize, value: S::Value) -> S::Displaced {
debug_assert!(pos < N);
let slot_ptr = unsafe { Self::slots_ptr(this).add(pos) };
unsafe { S::swap_init_raw_ptr(slot_ptr, value) }
}
#[inline]
pub unsafe fn move_raw<const M: usize>(
src: *const Self,
src_pos: usize,
dst: *const SlotArray<S, M>,
dst_pos: usize,
) {
debug_assert!(src_pos < N);
debug_assert!(dst_pos < M);
let src_ptr = unsafe { Self::slots_ptr(src).add(src_pos) };
let dst_ptr = unsafe { SlotArray::<S, M>::slots_ptr(dst).add(dst_pos) };
unsafe { S::move_init_to_empty_raw_ptr(src_ptr, dst_ptr) };
}
#[inline]
pub unsafe fn load_into<'a>(
&'a self,
pos: usize,
buf: &'a mut MaybeUninit<S::Value>,
) -> &'a S::Value {
debug_assert!(pos < N);
unsafe { (*self.slots.get()).get_unchecked(pos).load_into(buf) }
}
#[inline]
pub unsafe fn store_into_empty(&self, pos: usize, value: S::Value) {
debug_assert!(pos < N);
unsafe {
(*self.slots.get()).get_unchecked(pos).store_into_empty(value);
}
}
#[inline]
pub unsafe fn swap_init(&self, pos: usize, value: S::Value) -> S::Displaced {
debug_assert!(pos < N);
unsafe { (*self.slots.get()).get_unchecked(pos).swap_init(value) }
}
#[inline]
pub unsafe fn take_init(&self, pos: usize) -> S::Displaced {
debug_assert!(pos < N);
unsafe { (*self.slots.get()).get_unchecked(pos).take_init() }
}
#[inline]
pub unsafe fn shift_insert(&self, len: usize, pos: usize, value: S::Value) {
debug_assert!(pos <= len);
debug_assert!(len < N);
for i in (pos..len).rev() {
let src = unsafe { (*self.slots.get()).get_unchecked(i) };
let dst = unsafe { (*self.slots.get()).get_unchecked(i + 1) };
unsafe { src.move_init_to_empty(dst) };
}
unsafe {
(*self.slots.get()).get_unchecked(pos).store_into_empty(value);
}
}
#[inline]
pub unsafe fn shift_remove(&self, len: usize, pos: usize) -> S::Displaced {
debug_assert!(pos < len);
debug_assert!(len <= N);
let removed = unsafe { (*self.slots.get()).get_unchecked(pos).take_init() };
for i in pos..(len - 1) {
let src = unsafe { (*self.slots.get()).get_unchecked(i + 1) };
let dst = unsafe { (*self.slots.get()).get_unchecked(i) };
unsafe { src.move_init_to_empty(dst) };
}
removed
}
#[inline]
pub unsafe fn drop_initialized(&mut self, len: usize) {
debug_assert!(len <= N);
for i in 0..len {
unsafe { (*self.slots.get())[i].drop_in_place(true) };
}
for i in len..N {
unsafe { (*self.slots.get())[i].drop_in_place(false) };
}
}
}
#[repr(transparent)]
pub struct AtomicLen(AtomicU16);
impl AtomicLen {
#[inline]
pub const fn new(value: u16) -> Self {
Self(AtomicU16::new(value))
}
#[inline]
pub fn load(&self) -> u16 {
self.0.load(Ordering::Acquire)
}
#[inline]
pub fn load_relaxed(&self) -> u16 {
self.0.load(Ordering::Relaxed)
}
#[inline]
pub fn store(&self, value: u16) {
self.0.store(value, Ordering::Release);
}
#[inline]
pub fn fetch_add(&self, delta: u16) -> u16 {
self.0.fetch_add(delta, Ordering::AcqRel)
}
#[inline]
pub fn fetch_sub(&self, delta: u16) -> u16 {
self.0.fetch_sub(delta, Ordering::AcqRel)
}
#[inline]
pub unsafe fn fetch_add_relaxed(&self, delta: u16) -> u16 {
self.0.fetch_add(delta, Ordering::Relaxed)
}
#[inline]
pub unsafe fn fetch_sub_relaxed(&self, delta: u16) -> u16 {
self.0.fetch_sub(delta, Ordering::Relaxed)
}
#[inline]
pub unsafe fn store_relaxed(&self, value: u16) {
self.0.store(value, Ordering::Relaxed);
}
#[inline]
pub unsafe fn load_raw(this: *const Self) -> u16 {
let atomic = unsafe { &*(this as *const AtomicU16) };
atomic.load(Ordering::Acquire)
}
#[inline]
pub unsafe fn load_raw_relaxed(this: *const Self) -> u16 {
let atomic = unsafe { &*(this as *const AtomicU16) };
atomic.load(Ordering::Relaxed)
}
}
impl Default for AtomicLen {
#[inline]
fn default() -> Self {
Self::new(0)
}
}
impl core::fmt::Debug for AtomicLen {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "AtomicLen({})", self.load_relaxed())
}
}
pub struct OptimisticOption<S> {
present: AtomicU8,
slot: S,
}
impl<S: Default> Default for OptimisticOption<S> {
fn default() -> Self {
Self {
present: AtomicU8::new(0),
slot: S::default(),
}
}
}
impl<S: OptimisticSlot> OptimisticOption<S> {
#[inline]
pub unsafe fn load(&self) -> Option<S::Value> {
let present = self.present.load(Ordering::Acquire);
if present == 0 {
None
} else {
Some(unsafe { self.slot.load() })
}
}
#[inline]
pub unsafe fn load_raw(this: *const Self) -> Option<S::Value> {
let present_ptr = unsafe { ptr::addr_of!((*this).present) };
let slot_ptr = unsafe { ptr::addr_of!((*this).slot) };
let present = unsafe { (*present_ptr).load(Ordering::Acquire) };
if present == 0 {
None
} else {
Some(unsafe { (*slot_ptr).load() })
}
}
#[inline]
pub unsafe fn store_some_into_empty(&self, value: S::Value) {
debug_assert_eq!(self.present.load(Ordering::Relaxed), 0);
unsafe { self.slot.store_into_empty(value) };
self.present.store(1, Ordering::Release);
}
#[inline]
pub unsafe fn replace(&self, value: S::Value) -> Option<S::Displaced> {
let was_present = self.present.load(Ordering::Relaxed) != 0;
if was_present {
let old = unsafe { self.slot.swap_init(value) };
Some(old)
} else {
unsafe { self.slot.store_into_empty(value) };
self.present.store(1, Ordering::Release);
None
}
}
#[inline]
pub unsafe fn take(&self) -> Option<S::Displaced> {
let was_present = self.present.load(Ordering::Relaxed) != 0;
if was_present {
self.present.store(0, Ordering::Release);
Some(unsafe { self.slot.take_init() })
} else {
None
}
}
#[inline]
pub unsafe fn drop_in_place(&mut self) {
let was_present = *self.present.get_mut() != 0;
unsafe { self.slot.drop_in_place(was_present) };
*self.present.get_mut() = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn inline_slot_u64_roundtrip() {
let s: InlineSlot<u64> = InlineSlot::default();
unsafe { assert_eq!(s.load(), 0u64) };
unsafe { s.store_into_empty(42) };
unsafe { assert_eq!(s.load(), 42) };
let old = unsafe { s.swap_init(7) };
assert_eq!(old, 42);
unsafe { assert_eq!(s.load(), 7) };
let taken = unsafe { s.take_init() };
assert_eq!(taken, 7);
unsafe { assert_eq!(s.load(), 0) };
}
#[test]
fn inline_slot_i32_negative() {
let s: InlineSlot<i32> = InlineSlot::default();
unsafe { s.store_into_empty(-12345) };
unsafe { assert_eq!(s.load(), -12345) };
}
#[test]
fn boxed_slot_arc_roundtrip() {
let s: BoxedSlot<Arc<String>> = BoxedSlot::default();
let v = Arc::new("hello".to_string());
unsafe { s.store_into_empty(v.clone()) };
unsafe {
let read: Arc<String> = s.load();
assert_eq!(*read, "hello");
}
let new = Arc::new("world".to_string());
let old: Box<Arc<String>> = unsafe { s.swap_init(new) };
assert_eq!(**old, "hello");
unsafe { assert_eq!(*s.load(), "world") };
let taken: Box<Arc<String>> = unsafe { s.take_init() };
assert_eq!(**taken, "world");
}
#[test]
fn boxed_slot_drops_on_destruction() {
let counter = Arc::new(std::sync::atomic::AtomicUsize::new(0));
struct DropCounter(Arc<std::sync::atomic::AtomicUsize>);
impl Clone for DropCounter {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Drop for DropCounter {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::SeqCst);
}
}
{
let mut s: BoxedSlot<DropCounter> = BoxedSlot::default();
unsafe { s.store_into_empty(DropCounter(counter.clone())) };
unsafe { s.drop_in_place(true) };
}
assert!(counter.load(Ordering::SeqCst) >= 1);
}
#[test]
fn slot_array_shift_insert_remove() {
let arr: SlotArray<InlineSlot<u64>, 8> = SlotArray::default();
unsafe {
arr.store_into_empty(0, 10);
arr.store_into_empty(1, 20);
arr.store_into_empty(2, 30);
}
unsafe { arr.shift_insert(3, 1, 15) };
unsafe {
assert_eq!(arr.load(0), 10);
assert_eq!(arr.load(1), 15);
assert_eq!(arr.load(2), 20);
assert_eq!(arr.load(3), 30);
}
let removed = unsafe { arr.shift_remove(4, 2) };
assert_eq!(removed, 20);
unsafe {
assert_eq!(arr.load(0), 10);
assert_eq!(arr.load(1), 15);
assert_eq!(arr.load(2), 30);
}
}
#[test]
fn inline_slot_load_into_returns_buffer_borrow() {
use core::mem::MaybeUninit;
let s: InlineSlot<u64> = InlineSlot::default();
unsafe { s.store_into_empty(0x123456789abcdef0) };
let mut buf = MaybeUninit::uninit();
let v: &u64 = unsafe { s.load_into(&mut buf) };
assert_eq!(*v, 0x123456789abcdef0);
}
#[test]
fn boxed_slot_load_into_returns_box_borrow() {
use core::mem::MaybeUninit;
let s: BoxedSlot<String> = BoxedSlot::default();
unsafe { s.store_into_empty(String::from("hello")) };
let mut buf = MaybeUninit::uninit();
let v: &String = unsafe { s.load_into(&mut buf) };
assert_eq!(v.as_str(), "hello");
let _ = unsafe { s.take_init() };
}
#[test]
fn unit_slot_load_into_returns_unit() {
use core::mem::MaybeUninit;
let s = UnitSlot;
let mut buf = MaybeUninit::uninit();
let v: &() = unsafe { s.load_into(&mut buf) };
assert_eq!(*v, ());
}
#[test]
fn slot_array_raw_projection() {
let arr: SlotArray<InlineSlot<u64>, 4> = SlotArray::default();
unsafe {
arr.store_into_empty(0, 100);
arr.store_into_empty(1, 200);
}
let raw: *const SlotArray<InlineSlot<u64>, 4> = &arr;
unsafe {
assert_eq!(SlotArray::<InlineSlot<u64>, 4>::load_raw(raw, 0), 100);
assert_eq!(SlotArray::<InlineSlot<u64>, 4>::load_raw(raw, 1), 200);
}
}
#[test]
fn atomic_len_basics() {
let l = AtomicLen::new(0);
assert_eq!(l.load(), 0);
l.store(5);
assert_eq!(l.load(), 5);
assert_eq!(l.fetch_add(2), 5);
assert_eq!(l.load(), 7);
assert_eq!(l.fetch_sub(3), 7);
assert_eq!(l.load(), 4);
let raw: *const AtomicLen = &l;
assert_eq!(unsafe { AtomicLen::load_raw(raw) }, 4);
}
#[test]
fn optimistic_option_lifecycle() {
let opt: OptimisticOption<InlineSlot<u32>> = OptimisticOption::default();
unsafe { assert_eq!(opt.load(), None) };
unsafe { opt.store_some_into_empty(42) };
unsafe { assert_eq!(opt.load(), Some(42)) };
let prev: Option<u32> = unsafe { opt.replace(100) };
assert_eq!(prev, Some(42));
unsafe { assert_eq!(opt.load(), Some(100)) };
let taken: Option<u32> = unsafe { opt.take() };
assert_eq!(taken, Some(100));
unsafe { assert_eq!(opt.load(), None) };
}
#[test]
fn optimistic_option_raw_load() {
let opt: OptimisticOption<InlineSlot<u32>> = OptimisticOption::default();
unsafe { opt.store_some_into_empty(7) };
let raw: *const OptimisticOption<InlineSlot<u32>> = &opt;
unsafe {
assert_eq!(OptimisticOption::<InlineSlot<u32>>::load_raw(raw), Some(7));
}
}
use std::sync::Mutex;
use std::thread;
#[test]
fn inline_slot_concurrent_swap_vs_load() {
let slot: Arc<InlineSlot<u64>> = Arc::new(InlineSlot::default());
unsafe { slot.store_into_empty(1) };
let writer_iters = if cfg!(miri) {
32
} else {
4096
};
let reader_iters = if cfg!(miri) {
64
} else {
8192
};
let writer_lock: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
let writer = {
let slot = slot.clone();
let writer_lock = writer_lock.clone();
thread::spawn(move || {
for i in 1u64..=writer_iters {
let _g = writer_lock.lock().unwrap();
let _: u64 = unsafe { slot.swap_init(i + 1) };
}
})
};
let readers: Vec<_> = (0..3)
.map(|_| {
let slot = slot.clone();
thread::spawn(move || {
for _ in 0..reader_iters {
let v = unsafe { slot.load() };
assert!(v >= 1 && v <= writer_iters + 1);
}
})
})
.collect();
writer.join().unwrap();
for r in readers {
r.join().unwrap();
}
}
#[test]
fn boxed_slot_concurrent_load_after_store() {
let slot: Arc<BoxedSlot<Arc<String>>> = Arc::new(BoxedSlot::default());
let value = Arc::new("hello".to_string());
unsafe { slot.store_into_empty(value.clone()) };
let reader_iters = if cfg!(miri) {
32
} else {
4096
};
let readers: Vec<_> = (0..4)
.map(|_| {
let slot = slot.clone();
thread::spawn(move || {
for _ in 0..reader_iters {
let v: Arc<String> = unsafe { slot.load() };
assert_eq!(*v, "hello");
}
})
})
.collect();
for r in readers {
r.join().unwrap();
}
let _displaced: Box<Arc<String>> = unsafe { slot.take_init() };
}
#[test]
fn boxed_slot_concurrent_swap_with_deferred_drop() {
let slot: Arc<BoxedSlot<Arc<u64>>> = Arc::new(BoxedSlot::default());
unsafe { slot.store_into_empty(Arc::new(0)) };
let writer_iters = if cfg!(miri) {
16
} else {
1024
};
let reader_iters = if cfg!(miri) {
32
} else {
4096
};
let deferred: Arc<Mutex<Vec<Box<Arc<u64>>>>> = Arc::new(Mutex::new(Vec::new()));
let writer_lock: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
let writer = {
let slot = slot.clone();
let writer_lock = writer_lock.clone();
let deferred = deferred.clone();
thread::spawn(move || {
for i in 1u64..=writer_iters {
let _g = writer_lock.lock().unwrap();
let old: Box<Arc<u64>> = unsafe { slot.swap_init(Arc::new(i)) };
deferred.lock().unwrap().push(old);
}
})
};
let readers: Vec<_> = (0..3)
.map(|_| {
let slot = slot.clone();
thread::spawn(move || {
for _ in 0..reader_iters {
let v: Arc<u64> = unsafe { slot.load() };
assert!(*v <= writer_iters);
}
})
})
.collect();
writer.join().unwrap();
for r in readers {
r.join().unwrap();
}
drop(deferred);
}
#[test]
fn slot_array_concurrent_shift_vs_load_raw() {
const CAP: usize = 16;
let arr: Arc<SlotArray<InlineSlot<u64>, CAP>> = Arc::new(SlotArray::default());
for (i, v) in [1u64, 2, 3, 4].iter().enumerate() {
unsafe { arr.store_into_empty(i, *v) };
}
let initial_len = Arc::new(std::sync::atomic::AtomicUsize::new(4));
let writer_iters = if cfg!(miri) {
8
} else {
512
};
let reader_iters = if cfg!(miri) {
32
} else {
4096
};
let writer_lock: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
let writer = {
let arr = arr.clone();
let writer_lock = writer_lock.clone();
let initial_len = initial_len.clone();
thread::spawn(move || {
for i in 0..writer_iters {
let _g = writer_lock.lock().unwrap();
let len = initial_len.load(std::sync::atomic::Ordering::Relaxed);
if i % 2 == 0 && len < CAP {
unsafe { arr.shift_insert(len, 1, 100 + i as u64) };
initial_len.store(len + 1, std::sync::atomic::Ordering::Relaxed);
} else if len > 1 {
let _removed: u64 = unsafe { arr.shift_remove(len, 1) };
initial_len.store(len - 1, std::sync::atomic::Ordering::Relaxed);
}
}
})
};
let arr_for_readers = arr.clone();
let raw_ptr_addr = Arc::as_ptr(&arr_for_readers) as usize;
let readers: Vec<_> = (0..3)
.map(|_| {
let _slot = arr_for_readers.clone();
let raw = raw_ptr_addr;
thread::spawn(move || {
let this: *const SlotArray<InlineSlot<u64>, CAP> = raw as *const _;
for _ in 0..reader_iters {
let v = unsafe { SlotArray::load_raw(this, 0) };
assert_eq!(v, 1);
}
})
})
.collect();
writer.join().unwrap();
for r in readers {
r.join().unwrap();
}
}
#[test]
fn atomic_len_concurrent_writers_readers() {
let len: Arc<AtomicLen> = Arc::new(AtomicLen::new(0));
let writers_n = 2;
let writer_iters = if cfg!(miri) {
32
} else {
4096
};
let reader_iters = if cfg!(miri) {
32
} else {
4096
};
let writers: Vec<_> = (0..writers_n)
.map(|_| {
let len = len.clone();
thread::spawn(move || {
for _ in 0..writer_iters {
len.fetch_add(1);
}
})
})
.collect();
let len_raw = Arc::as_ptr(&len) as usize;
let readers: Vec<_> = (0..2)
.map(|_| {
let len = len.clone();
let raw = len_raw;
thread::spawn(move || {
let this: *const AtomicLen = raw as *const _;
for _ in 0..reader_iters {
let a = len.load();
let b = unsafe { AtomicLen::load_raw(this) };
let expected_max = (writers_n * writer_iters) as u16;
assert!(a <= expected_max);
assert!(b <= expected_max);
}
})
})
.collect();
for w in writers {
w.join().unwrap();
}
for r in readers {
r.join().unwrap();
}
assert_eq!(len.load(), (writers_n * writer_iters) as u16);
}
#[test]
fn optimistic_option_concurrent_replace_vs_load() {
let opt: Arc<OptimisticOption<InlineSlot<u32>>> = Arc::new(OptimisticOption::default());
unsafe { opt.store_some_into_empty(0) };
let writer_iters = if cfg!(miri) {
32
} else {
4096
};
let reader_iters = if cfg!(miri) {
32
} else {
4096
};
let writer_lock: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
let writer = {
let opt = opt.clone();
let writer_lock = writer_lock.clone();
thread::spawn(move || {
for i in 1u32..=writer_iters {
let _g = writer_lock.lock().unwrap();
let _old: Option<u32> = unsafe { opt.replace(i) };
}
})
};
let readers: Vec<_> = (0..3)
.map(|_| {
let opt = opt.clone();
thread::spawn(move || {
for _ in 0..reader_iters {
let v = unsafe { opt.load() };
let n = v.expect("option always Some in this test");
assert!(n <= writer_iters);
}
})
})
.collect();
writer.join().unwrap();
for r in readers {
r.join().unwrap();
}
}
}