use crate::free_slots::FreeSlots;
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicUsize, Ordering};
pub struct CasSlots<const N: usize> {
next: UnsafeCell<MaybeUninit<[AtomicUsize; N]>>,
head: AtomicUsize,
}
const fn sentinel<const N: usize>() -> usize {
N
}
impl<const N: usize> CasSlots<N> {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
next: UnsafeCell::new(MaybeUninit::uninit()),
head: AtomicUsize::new(sentinel::<N>()),
}
}
#[inline]
unsafe fn next_array(&self) -> &[AtomicUsize; N] {
unsafe { &*(*self.next.get()).as_ptr() }
}
}
impl<const N: usize> FreeSlots<N> for CasSlots<N> {
fn new() -> Self {
Self::new()
}
fn fill(&self, count: usize) {
debug_assert!(count <= N, "fill count must not exceed capacity");
unsafe {
let next_ptr: *mut AtomicUsize = (*self.next.get()).as_mut_ptr().cast::<AtomicUsize>();
for i in 0..N {
let next = if i < count {
if i == 0 { sentinel::<N>() } else { i - 1 }
} else {
sentinel::<N>()
};
next_ptr.add(i).write(AtomicUsize::new(next));
}
}
let head_val = if count > 0 {
count - 1
} else {
sentinel::<N>()
};
self.head.store(head_val, Ordering::Release);
}
fn take(&self) -> Option<usize> {
let next = unsafe { self.next_array() };
loop {
let head = self.head.load(Ordering::Acquire);
if head == sentinel::<N>() {
return None;
}
let next_val = next[head].load(Ordering::Acquire);
match self
.head
.compare_exchange(head, next_val, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return Some(head),
Err(_) => continue,
}
}
}
fn put(&self, idx: usize) {
let next = unsafe { self.next_array() };
loop {
let head = self.head.load(Ordering::Acquire);
next[idx].store(head, Ordering::Release);
match self
.head
.compare_exchange(head, idx, Ordering::AcqRel, Ordering::Acquire)
{
Ok(_) => return,
Err(_) => continue,
}
}
}
}
unsafe impl<const N: usize> Sync for CasSlots<N> {}