use crate::stub::{AtomicBool, Ordering};
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cell::UnsafeCell;
pub trait Reclaim {
fn reclaim(&mut self);
}
struct LeaseSlot<T: Default + Reclaim> {
leased: AtomicBool,
item: UnsafeCell<T>,
}
impl<T: Default + Reclaim> Default for LeaseSlot<T> {
fn default() -> Self {
Self {
leased: AtomicBool::new(false),
item: UnsafeCell::new(T::default()),
}
}
}
unsafe impl<T: Default + Reclaim> Send for LeaseSlot<T> {}
unsafe impl<T: Default + Reclaim> Sync for LeaseSlot<T> {}
#[derive(Default)]
pub struct LeasePool<T: Default + Reclaim> {
items: Vec<Box<LeaseSlot<T>>>,
}
impl<T: Default + Reclaim> core::fmt::Debug for LeasePool<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LeasePool")
.field("items", &self.items.len())
.finish()
}
}
impl<T: Default + Reclaim> LeasePool<T> {
pub fn with_capacity(capacity: usize) -> Self {
Self {
items: Vec::with_capacity(capacity),
}
}
pub fn acquire(&mut self) -> LeaseHandle<T> {
let item: &LeaseSlot<T> = match self.items.iter().find(|item| {
item.leased
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}) {
Some(item) => item,
None => {
let item = Box::new(LeaseSlot::default());
item.leased.store(true, Ordering::Release);
self.items.push(item);
self.items.last().unwrap()
}
};
LeaseHandle { item }
}
}
pub struct LeaseHandle<T: Default + Reclaim> {
item: *const LeaseSlot<T>,
}
unsafe impl<T: Default + Reclaim> Send for LeaseHandle<T> {}
impl<T: Default + Reclaim> core::ops::Deref for LeaseHandle<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*(*self.item).item.get() }
}
}
impl<T: Default + Reclaim> core::ops::DerefMut for LeaseHandle<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(*self.item).item.get() }
}
}
impl<T: Default + Reclaim> core::fmt::Debug for LeaseHandle<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LeaseHandle").finish_non_exhaustive()
}
}
impl<T: Default + Reclaim> Drop for LeaseHandle<T> {
fn drop(&mut self) {
let item = unsafe { &*self.item };
unsafe { &mut *item.item.get() }.reclaim();
item.leased.store(false, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{vec, vec::Vec};
#[derive(Default)]
struct Probe(Vec<u32>);
impl Reclaim for Probe {
fn reclaim(&mut self) {
self.0.clear();
}
}
impl<T: Default + Reclaim> LeasePool<T> {
fn len(&self) -> usize {
self.items.len()
}
}
#[test]
fn growth_keeps_live_handles_valid() {
let mut pool = LeasePool::<Probe>::with_capacity(0);
let mut a = pool.acquire();
a.0.push(1);
let mut b = pool.acquire();
b.0.push(2);
let mut c = pool.acquire();
c.0.push(3);
a.0.push(10);
assert_eq!(a.0, vec![1, 10]);
assert_eq!(b.0, vec![2]);
assert_eq!(c.0, vec![3]);
}
#[test]
fn drop_reclaims_and_reuses_allocation() {
let mut pool = LeasePool::<Probe>::with_capacity(0);
let mut a = pool.acquire();
a.0.extend([1, 2, 3, 4]);
let cap = a.0.capacity();
assert!(cap >= 4);
drop(a);
let reused = pool.acquire();
assert!(reused.0.is_empty());
assert_eq!(reused.0.capacity(), cap);
}
#[test]
fn frees_slot_for_later_acquire() {
let mut pool = LeasePool::<Probe>::with_capacity(0);
for _ in 0..8 {
let mut handle = pool.acquire();
handle.0.push(0);
}
assert_eq!(pool.len(), 1);
}
}