use alloc::alloc::{dealloc, handle_alloc_error, Layout};
use core::{
cell::UnsafeCell,
marker::PhantomData,
ops::Deref,
ptr::{self, drop_in_place, NonNull},
sync::atomic::{self, AtomicUsize},
task::Waker,
};
use futures_util::task::AtomicWaker;
pub(crate) struct ArcSlice {
ptr: NonNull<ArcSliceInner>,
phantom: PhantomData<ArcSliceInner>,
}
#[repr(C)]
pub(crate) struct ArcSliceInner {
meta: ArcSliceInnerMeta,
slice: [ArcSlotInner],
}
pub(crate) struct ArcSliceInnerMeta {
strong: AtomicUsize,
waker: AtomicWaker,
list_head: AtomicUsize,
list_tail: UnsafeCell<usize>,
len: usize,
}
#[repr(C)]
pub(crate) struct ArcSlotInner {
index: usize,
next: AtomicUsize,
}
const fn __assert_send_sync<T: Send + Sync>() {}
const _: () = {
__assert_send_sync::<ArcSlotInner>();
unsafe impl Send for ArcSlice {}
unsafe impl Sync for ArcSlice {}
};
impl Deref for ArcSlice {
type Target = ArcSliceInner;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.as_ref() }
}
}
impl ArcSlice {
pub(crate) fn register(&self, waker: &Waker) {
self.meta.waker.register(waker)
}
fn get(&self, index: usize) -> Waker {
self.meta.inc_strong();
let ptr: *mut ArcSliceInner = NonNull::as_ptr(self.ptr);
let slot = unsafe { (ptr::addr_of_mut!((*ptr).slice) as *mut ArcSlotInner).add(index) };
debug_assert_eq!(
unsafe { (*slot).index },
index,
"the slot should point at our index"
);
slot::waker(slot)
}
pub(crate) unsafe fn pop(&self) -> ReadySlot<(usize, Waker)> {
match ArcSliceInner::pop(self) {
ReadySlot::Ready(i) => ReadySlot::Ready((i, self.get(i))),
ReadySlot::Inconsistent => ReadySlot::Inconsistent,
ReadySlot::None => ReadySlot::None,
}
}
}
impl ArcSliceInner {
pub(crate) unsafe fn push(&self, index: usize) {
self.slice
.get_unchecked(index)
.next
.store(self.meta.len + 1, atomic::Ordering::Relaxed);
let prev = self.meta.list_head.swap(index, atomic::Ordering::AcqRel);
self.slice
.get_unchecked(prev)
.next
.store(index, atomic::Ordering::Release);
}
unsafe fn pop(&self) -> ReadySlot<usize> {
let mut tail = *self.meta.list_tail.get();
let mut next = self.slice[tail].next.load(atomic::Ordering::Acquire);
if tail == self.meta.len {
if next > self.meta.len {
return ReadySlot::None;
}
*self.meta.list_tail.get() = next;
tail = next;
next = self
.slice
.get_unchecked(next)
.next
.load(atomic::Ordering::Acquire);
}
if next <= self.meta.len {
*self.meta.list_tail.get() = next;
debug_assert!(tail != self.meta.len);
return ReadySlot::Ready(tail);
}
if self.meta.list_head.load(atomic::Ordering::Acquire) != tail {
return ReadySlot::Inconsistent;
}
self.push(self.meta.len);
next = self
.slice
.get_unchecked(tail)
.next
.load(atomic::Ordering::Acquire);
if next <= self.meta.len {
*self.meta.list_tail.get() = next;
return ReadySlot::Ready(tail);
}
ReadySlot::Inconsistent
}
}
pub(crate) enum ReadySlot<T> {
Ready(T),
Inconsistent,
None,
}
mod slot {
use core::{
alloc::Layout,
mem::align_of,
ptr,
task::{RawWaker, RawWakerVTable, Waker},
};
use super::{ArcSliceInner, ArcSliceInnerMeta, ArcSlotInner};
unsafe fn meta_raw(ptr: *mut ArcSlotInner) -> *mut ArcSliceInnerMeta {
fn padding_needed_for(layout: &Layout, align: usize) -> usize {
let len = layout.size();
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
len_rounded_up.wrapping_sub(len)
}
let index = (*ptr).index;
let slice_start = ptr.sub(index);
let layout = Layout::new::<ArcSliceInnerMeta>();
let offset = layout.size() + padding_needed_for(&layout, align_of::<ArcSlotInner>());
unsafe { slice_start.cast::<u8>().sub(offset) }.cast::<ArcSliceInnerMeta>()
}
unsafe fn meta_ref<'a>(ptr: *const ArcSlotInner) -> &'a ArcSliceInnerMeta {
unsafe { &*meta_raw(ptr as *mut ArcSlotInner) }
}
unsafe fn inner_ref<'a>(ptr: *const ArcSlotInner) -> &'a ArcSliceInner {
let ptr = meta_raw(ptr as *mut ArcSlotInner);
let len = *core::ptr::addr_of!((*ptr).len);
let ptr = ptr as *const ArcSliceInnerMeta as *const ArcSlotInner;
&*(ptr::slice_from_raw_parts(ptr, len + 1) as *const ArcSliceInner)
}
pub(super) fn waker(ptr: *const ArcSlotInner) -> Waker {
static VTABLE: RawWakerVTable =
RawWakerVTable::new(clone_waker, wake, wake_by_ref, drop_waker);
unsafe fn clone_waker(waker: *const ()) -> RawWaker {
meta_ref(waker.cast()).inc_strong();
RawWaker::new(waker, &VTABLE)
}
unsafe fn wake(waker: *const ()) {
wake_by_ref(waker);
drop_waker(waker);
}
unsafe fn wake_by_ref(waker: *const ()) {
let slot = waker.cast();
let inner = inner_ref(slot);
inner.push((*slot).index);
inner.meta.waker.wake();
}
unsafe fn drop_waker(waker: *const ()) {
let meta = meta_ref(waker.cast());
if meta.dec_strong() {
unsafe {
super::drop_inner(meta_raw(waker.cast::<ArcSlotInner>() as *mut _), meta.len)
}
}
}
let raw_waker = RawWaker::new(ptr as *const (), &VTABLE);
unsafe { Waker::from_raw(raw_waker) }
}
}
impl ArcSliceInnerMeta {
fn inc_strong(&self) {
let old_size = self
.strong
.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
if old_size > (isize::MAX) as usize {
abort("too many arc clones");
}
}
fn dec_strong(&self) -> bool {
let old_size = self
.strong
.fetch_sub(1, core::sync::atomic::Ordering::Release);
if old_size != 1 {
return false;
}
atomic::fence(atomic::Ordering::Acquire);
true
}
}
unsafe fn drop_inner(p: *mut ArcSliceInnerMeta, capacity: usize) {
let layout = ArcSlice::layout(capacity);
drop_in_place(p);
dealloc(p.cast(), layout);
}
impl Drop for ArcSlice {
fn drop(&mut self) {
if self.meta.dec_strong() {
unsafe { drop_inner(self.ptr.as_ptr().cast(), self.meta.len) }
}
}
}
impl ArcSlice {
pub(crate) fn new(cap: usize) -> Self {
let arc_slice_layout = Self::layout(cap);
debug_assert!(arc_slice_layout.size() > 0);
let ptr = unsafe { alloc::alloc::alloc(arc_slice_layout) };
if ptr.is_null() {
handle_alloc_error(arc_slice_layout)
}
let inner = ptr::slice_from_raw_parts_mut(ptr.cast::<ArcSlotInner>(), cap + 1)
as *mut ArcSliceInner;
debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, arc_slice_layout);
unsafe {
let meta = ArcSliceInnerMeta {
strong: AtomicUsize::new(1),
len: cap,
list_head: AtomicUsize::new(cap),
list_tail: UnsafeCell::new(cap),
waker: AtomicWaker::new(),
};
ptr::write(ptr::addr_of_mut!((*inner).meta), meta);
for i in 0..cap {
ptr::write(
ptr::addr_of_mut!((*inner).slice[i]),
ArcSlotInner {
index: i,
next: AtomicUsize::new(cap + 1),
},
);
}
}
Self {
ptr: unsafe { NonNull::new_unchecked(inner) },
phantom: PhantomData,
}
}
fn layout(cap: usize) -> Layout {
let padded = Layout::new::<ArcSlotInner>().pad_to_align();
let alloc_size = padded.size().checked_mul(cap + 1).unwrap();
let slice_layout =
Layout::from_size_align(alloc_size, Layout::new::<ArcSlotInner>().align()).unwrap();
Layout::new::<ArcSliceInnerMeta>()
.extend(slice_layout)
.unwrap()
.0
.pad_to_align()
}
}
fn abort(s: &str) -> ! {
struct DoublePanic;
impl Drop for DoublePanic {
fn drop(&mut self) {
panic!("panicking twice to abort the program");
}
}
let _bomb = DoublePanic;
panic!("{}", s);
}