#![allow(deprecated)]
use std::sync::{Arc, Mutex};
use arc_swap::ArcSwap;
use crate::concurrent::LockFreeHandle;
const PAGE_BITS: u32 = 6;
const PAGE: usize = 1 << PAGE_BITS;
enum SlotState<T> {
Occupied(Arc<T>),
Vacant { next_free: Option<u32> },
}
impl<T> Clone for SlotState<T> {
fn clone(&self) -> Self {
match self {
Self::Occupied(v) => Self::Occupied(Arc::clone(v)),
Self::Vacant { next_free } => Self::Vacant {
next_free: *next_free,
},
}
}
}
struct Slot<T> {
generation: u32,
state: SlotState<T>,
}
impl<T> Clone for Slot<T> {
fn clone(&self) -> Self {
Self {
generation: self.generation,
state: self.state.clone(),
}
}
}
struct Snapshot<T> {
pages: Vec<Arc<Vec<Slot<T>>>>,
free_head: Option<u32>,
len: usize,
}
impl<T> Clone for Snapshot<T> {
fn clone(&self) -> Self {
Self {
pages: self.pages.clone(),
free_head: self.free_head,
len: self.len,
}
}
}
#[deprecated(
since = "0.1.0",
note = "concurrent regions are legacy/research-tier; use the production allocator stack (`alloc-xthread`) for cross-thread allocation needs"
)]
pub struct LockFreeRegion<T> {
state: ArcSwap<Snapshot<T>>,
writers: Mutex<()>,
}
impl<T> LockFreeRegion<T> {
#[must_use]
pub fn new() -> Self {
Self {
state: ArcSwap::new(Arc::new(Snapshot {
pages: Vec::new(),
free_head: None,
len: 0,
})),
writers: Mutex::new(()),
}
}
#[must_use]
pub fn with_pages(page_count: usize) -> Self {
let page_len = u32::try_from(PAGE).expect("PAGE fits u32");
let mut pages: Vec<Arc<Vec<Slot<T>>>> = Vec::with_capacity(page_count);
let mut free_head: Option<u32> = None;
let total_pages = u32::try_from(page_count).unwrap_or(0);
for page_idx in 0..total_pages {
let base = page_idx
.checked_mul(page_len)
.expect("page base overflows u32");
let mut slots: Vec<Slot<T>> = Vec::with_capacity(PAGE);
for off in 0..page_len {
let global = base
.checked_add(off)
.expect("global slot index overflows u32");
let next_free = if global + 1 >= total_pages * page_len {
None
} else {
Some(global + 1)
};
slots.push(Slot {
generation: 0,
state: SlotState::Vacant { next_free },
});
}
if page_idx == 0 {
free_head = Some(base);
}
pages.push(Arc::new(slots));
}
Self {
state: ArcSwap::new(Arc::new(Snapshot {
pages,
free_head,
len: 0,
})),
writers: Mutex::new(()),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.state.load().len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.state.load().len == 0
}
#[must_use]
pub fn get(&self, handle: LockFreeHandle<T>) -> Option<Arc<T>> {
let snap = self.state.load();
let page = snap.pages.get((handle.index >> PAGE_BITS) as usize)?;
let slot = &page[(handle.index as usize) & (PAGE - 1)];
if slot.generation != handle.generation {
return None;
}
match &slot.state {
SlotState::Occupied(v) => Some(Arc::clone(v)),
SlotState::Vacant { .. } => None,
}
}
#[must_use]
pub fn contains(&self, handle: LockFreeHandle<T>) -> bool {
self.get(handle).is_some()
}
pub fn insert(&self, value: T) -> LockFreeHandle<T> {
let _guard = self.writers.lock().expect("writer mutex poisoned");
let cur = self.state.load_full();
let mut next: Snapshot<T> = (*cur).clone();
let value = Arc::new(value);
let (index, generation) = match next.free_head {
Some(head) => insert_reusing(&mut next, head, value),
None => insert_growing(&mut next, value),
};
next.len += 1;
self.state.store(Arc::new(next));
LockFreeHandle::new(index, generation)
}
pub fn remove(&self, handle: LockFreeHandle<T>) -> Option<Arc<T>> {
let _guard = self.writers.lock().expect("writer mutex poisoned");
let cur = self.state.load_full();
let mut next: Snapshot<T> = (*cur).clone();
let page_idx = (handle.index >> PAGE_BITS) as usize;
let off = (handle.index as usize) & (PAGE - 1);
let page_arc = next.pages.get(page_idx)?;
let mut new_page: Vec<Slot<T>> = (**page_arc).clone();
let slot = &mut new_page[off];
if slot.generation != handle.generation {
return None;
}
let value = match core::mem::replace(&mut slot.state, SlotState::Vacant { next_free: None })
{
SlotState::Occupied(v) => v,
SlotState::Vacant { .. } => return None,
};
if slot.generation < u32::MAX {
slot.generation += 1;
slot.state = SlotState::Vacant {
next_free: next.free_head,
};
next.free_head = Some(handle.index);
}
next.pages[page_idx] = Arc::new(new_page);
next.len -= 1;
self.state.store(Arc::new(next));
Some(value)
}
}
impl<T> Default for LockFreeRegion<T> {
fn default() -> Self {
Self::new()
}
}
fn insert_reusing<T>(next: &mut Snapshot<T>, head: u32, value: Arc<T>) -> (u32, u32) {
let page_idx = (head >> PAGE_BITS) as usize;
let off = (head as usize) & (PAGE - 1);
let mut new_page: Vec<Slot<T>> = (*next.pages[page_idx]).clone();
let slot = &mut new_page[off];
let gen = slot.generation;
let old_state = std::mem::replace(&mut slot.state, SlotState::Vacant { next_free: None });
let SlotState::Vacant {
next_free: advanced_head,
} = old_state
else {
unreachable!("free_head pointed at an Occupied slot — free list corrupted")
};
slot.state = SlotState::Occupied(value);
next.pages[page_idx] = Arc::new(new_page);
next.free_head = advanced_head;
(head, gen)
}
fn insert_growing<T>(next: &mut Snapshot<T>, value: Arc<T>) -> (u32, u32) {
let page_idx = u32::try_from(next.pages.len()).expect("page count overflows u32");
let page_len = u32::try_from(PAGE).expect("PAGE fits u32");
let base = page_idx
.checked_mul(page_len)
.expect("new page base overflows u32");
let mut new_page: Vec<Slot<T>> = Vec::with_capacity(PAGE);
new_page.push(Slot {
generation: 0,
state: SlotState::Vacant { next_free: None },
});
for off in 1..page_len {
let global = base
.checked_add(off)
.expect("global slot index overflows u32");
let next_in_page = if off + 1 == page_len {
None
} else {
Some(global + 1)
};
new_page.push(Slot {
generation: 0,
state: SlotState::Vacant {
next_free: next_in_page,
},
});
}
let claimed = base;
new_page[0].state = SlotState::Occupied(value);
next.pages.push(Arc::new(new_page));
next.free_head = (page_len > 1).then_some(base + 1);
(claimed, 0)
}