use std::cell::UnsafeCell;
use std::mem::{ManuallyDrop, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::rc::{Rc, Weak};
#[repr(C)]
struct Inner<T> {
data: UnsafeCell<Vec<T>>,
#[allow(clippy::type_complexity)]
reset: UnsafeCell<Box<dyn FnMut(&mut T)>>,
#[allow(clippy::type_complexity)]
factory: UnsafeCell<MaybeUninit<Box<dyn FnMut() -> T>>>,
}
impl<T> Inner<T> {
fn new_bounded<R>(data: Vec<T>, reset: R) -> Self
where
R: FnMut(&mut T) + 'static,
{
Self {
data: UnsafeCell::new(data),
reset: UnsafeCell::new(Box::new(reset)),
factory: UnsafeCell::new(MaybeUninit::uninit()),
}
}
fn new_growable<F, R>(data: Vec<T>, factory: F, reset: R) -> Self
where
F: FnMut() -> T + 'static,
R: FnMut(&mut T) + 'static,
{
Self {
data: UnsafeCell::new(data),
reset: UnsafeCell::new(Box::new(reset)),
factory: UnsafeCell::new(MaybeUninit::new(Box::new(factory))),
}
}
fn try_pop(&self) -> Option<T> {
let data = unsafe { &mut *self.data.get() };
data.pop()
}
#[allow(clippy::option_if_let_else)]
unsafe fn pop_or_create(&self) -> T {
unsafe {
let data = &mut *self.data.get();
if let Some(value) = data.pop() {
value
} else {
let factory = &mut *self.factory.get();
(factory.assume_init_mut())()
}
}
}
fn return_value(&self, value: &mut T) {
let reset = unsafe { &mut *self.reset.get() };
reset(value);
}
fn push(&self, value: T) {
let data = unsafe { &mut *self.data.get() };
data.push(value);
}
fn available(&self) -> usize {
unsafe { (*self.data.get()).len() }
}
fn is_empty(&self) -> bool {
self.available() == 0
}
}
pub struct BoundedPool<T> {
inner: Rc<Inner<T>>,
}
impl<T> BoundedPool<T> {
pub fn new<I, R>(capacity: usize, mut init: I, reset: R) -> Self
where
I: FnMut() -> T,
R: FnMut(&mut T) + 'static,
{
assert!(capacity > 0, "capacity must be non-zero");
let mut data = Vec::with_capacity(capacity);
for _ in 0..capacity {
data.push(init());
}
Self {
inner: Rc::new(Inner::new_bounded(data, reset)),
}
}
pub fn try_acquire(&self) -> Option<Pooled<T>> {
self.inner.try_pop().map(|value| Pooled {
value: ManuallyDrop::new(value),
inner: Rc::downgrade(&self.inner),
})
}
pub fn available(&self) -> usize {
self.inner.available()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
pub struct Pool<T> {
inner: Rc<Inner<T>>,
}
impl<T> Pool<T> {
pub fn new<F, R>(factory: F, reset: R) -> Self
where
F: FnMut() -> T + 'static,
R: FnMut(&mut T) + 'static,
{
Self {
inner: Rc::new(Inner::new_growable(Vec::new(), factory, reset)),
}
}
pub fn with_capacity<F, R>(capacity: usize, mut factory: F, reset: R) -> Self
where
F: FnMut() -> T + 'static,
R: FnMut(&mut T) + 'static,
{
let mut data = Vec::with_capacity(capacity);
for _ in 0..capacity {
data.push(factory());
}
Self {
inner: Rc::new(Inner::new_growable(data, factory, reset)),
}
}
pub fn acquire(&self) -> Pooled<T> {
let value = unsafe { self.inner.pop_or_create() };
Pooled {
value: ManuallyDrop::new(value),
inner: Rc::downgrade(&self.inner),
}
}
pub fn try_acquire(&self) -> Option<Pooled<T>> {
self.inner.try_pop().map(|value| Pooled {
value: ManuallyDrop::new(value),
inner: Rc::downgrade(&self.inner),
})
}
pub fn take(&self) -> T {
unsafe { self.inner.pop_or_create() }
}
pub fn try_take(&self) -> Option<T> {
self.inner.try_pop()
}
pub fn put(&self, mut value: T) {
self.inner.return_value(&mut value);
self.inner.push(value);
}
pub fn available(&self) -> usize {
self.inner.available()
}
}
impl<T> Drop for Pool<T> {
fn drop(&mut self) {
unsafe {
let factory = &mut *self.inner.factory.get();
factory.assume_init_drop();
}
}
}
#[must_use = "dropping the guard immediately returns the object to the pool"]
pub struct Pooled<T> {
value: ManuallyDrop<T>,
inner: Weak<Inner<T>>,
}
impl<T> Deref for Pooled<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.value
}
}
impl<T> DerefMut for Pooled<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T> Drop for Pooled<T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.upgrade() {
inner.return_value(&mut self.value);
let value = unsafe { ManuallyDrop::take(&mut self.value) };
inner.push(value);
} else {
unsafe { ManuallyDrop::drop(&mut self.value) };
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::rc::Rc as StdRc;
#[test]
fn bounded_pool_basic() {
let pool = BoundedPool::new(3, || Vec::<u8>::with_capacity(16), Vec::clear);
assert_eq!(pool.available(), 3);
let mut a = pool.try_acquire().unwrap();
assert_eq!(pool.available(), 2);
a.extend_from_slice(b"hello");
assert_eq!(&*a, b"hello");
let _b = pool.try_acquire().unwrap();
let _c = pool.try_acquire().unwrap();
assert_eq!(pool.available(), 0);
assert!(pool.try_acquire().is_none());
drop(a);
assert_eq!(pool.available(), 1);
let d = pool.try_acquire().unwrap();
assert!(d.is_empty()); }
#[test]
fn bounded_pool_reset_called() {
let reset_count = StdRc::new(Cell::new(0));
let reset_count_clone = reset_count.clone();
let pool = BoundedPool::new(
2,
|| 0u32,
move |_| {
reset_count_clone.set(reset_count_clone.get() + 1);
},
);
let a = pool.try_acquire().unwrap();
assert_eq!(reset_count.get(), 0);
drop(a);
assert_eq!(reset_count.get(), 1);
let b = pool.try_acquire().unwrap();
let c = pool.try_acquire().unwrap();
drop(b);
drop(c);
assert_eq!(reset_count.get(), 3);
}
#[test]
fn bounded_pool_outlives_guard() {
let guard;
{
let pool = BoundedPool::new(1, || String::from("test"), String::clear);
guard = pool.try_acquire().unwrap();
}
assert_eq!(&*guard, "test");
drop(guard);
}
#[test]
fn growable_pool_basic() {
let pool = Pool::new(|| Vec::<u8>::with_capacity(16), Vec::clear);
assert_eq!(pool.available(), 0);
let mut a = pool.acquire();
a.extend_from_slice(b"hello");
drop(a);
assert_eq!(pool.available(), 1);
let b = pool.acquire();
assert!(b.is_empty()); assert_eq!(pool.available(), 0);
}
#[test]
fn growable_pool_try_acquire() {
let pool = Pool::new(|| 42u32, |_| {});
assert!(pool.try_acquire().is_none());
let a = pool.acquire();
drop(a);
let b = pool.try_acquire().unwrap();
assert_eq!(*b, 42);
}
#[test]
fn growable_pool_with_capacity() {
let pool = Pool::with_capacity(5, String::new, String::clear);
assert_eq!(pool.available(), 5);
let _a = pool.try_acquire().unwrap();
let _b = pool.try_acquire().unwrap();
assert_eq!(pool.available(), 3);
}
#[test]
fn growable_pool_outlives_guard() {
let guard;
{
let pool = Pool::new(|| String::from("test"), String::clear);
guard = pool.acquire();
}
assert_eq!(&*guard, "test");
drop(guard);
}
#[test]
#[should_panic(expected = "capacity must be non-zero")]
fn bounded_pool_zero_capacity_panics() {
let _ = BoundedPool::new(0, || (), |()| {});
}
#[test]
fn take_put_basic() {
let pool = Pool::new(|| Vec::<u8>::with_capacity(16), Vec::clear);
let mut buf = pool.take();
buf.extend_from_slice(b"hello");
assert_eq!(&buf, b"hello");
pool.put(buf);
assert_eq!(pool.available(), 1);
let reused = pool.take();
assert!(reused.is_empty()); }
#[test]
fn try_take_empty_returns_none() {
let pool = Pool::new(|| 0u32, |_| {});
assert!(pool.try_take().is_none());
let v = pool.take(); pool.put(v);
assert!(pool.try_take().is_some());
}
#[test]
fn take_put_reset_called() {
let reset_count = StdRc::new(Cell::new(0));
let rc = reset_count.clone();
let pool = Pool::new(
|| 0u32,
move |_| {
rc.set(rc.get() + 1);
},
);
let v = pool.take();
assert_eq!(reset_count.get(), 0);
pool.put(v);
assert_eq!(reset_count.get(), 1);
let v = pool.take();
pool.put(v);
assert_eq!(reset_count.get(), 2);
}
#[test]
fn take_put_with_capacity() {
let pool = Pool::with_capacity(5, || String::from("init"), String::clear);
assert_eq!(pool.available(), 5);
let s = pool.try_take().unwrap();
assert_eq!(s, "init");
assert_eq!(pool.available(), 4);
pool.put(s);
assert_eq!(pool.available(), 5);
}
#[test]
fn mix_raii_and_manual() {
let pool = Pool::with_capacity(3, Vec::<u8>::new, Vec::clear);
let mut manual = pool.take();
manual.push(1);
let mut guard = pool.acquire();
guard.push(2);
assert_eq!(pool.available(), 1);
pool.put(manual);
assert_eq!(pool.available(), 2);
drop(guard);
assert_eq!(pool.available(), 3);
}
}