#![doc = include_str!("README.md")]
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex};
struct PoolInner<T> {
items: Mutex<Vec<T>>,
}
pub struct ObjectPool<T> {
inner: Arc<PoolInner<T>>,
}
impl<T> Clone for ObjectPool<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
pub struct Pooled<'a, T> {
item: Option<T>,
pool: &'a ObjectPool<T>,
}
impl<T> ObjectPool<T> {
pub fn new<F>(capacity: usize, mut init: F) -> Self
where
F: FnMut() -> T,
{
let mut items = Vec::with_capacity(capacity);
for _ in 0..capacity {
items.push(init());
}
Self {
inner: Arc::new(PoolInner {
items: Mutex::new(items),
}),
}
}
pub fn try_get(&'_ self) -> Option<Pooled<'_, T>> {
let item = self.inner.items.lock().unwrap().pop()?;
Some(Pooled {
item: Some(item),
pool: self,
})
}
fn put(&self, item: T) {
self.inner.items.lock().unwrap().push(item);
}
pub fn available(&self) -> usize {
self.inner.items.lock().unwrap().len()
}
}
impl<'a, T> Deref for Pooled<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.item.as_ref().unwrap()
}
}
impl<'a, T> DerefMut for Pooled<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.item.as_mut().unwrap()
}
}
impl<'a, T> Drop for Pooled<'a, T> {
fn drop(&mut self) {
if let Some(item) = self.item.take() {
self.pool.put(item);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
struct Order {
id: u64,
#[allow(dead_code)] price: f64,
}
fn new_order() -> Order {
Order { id: 0, price: 0.0 }
}
#[test]
fn test_get_and_put() {
let pool = ObjectPool::new(2, new_order);
assert_eq!(pool.available(), 2);
let mut order1 = pool.try_get().unwrap();
assert_eq!(pool.available(), 1);
order1.id = 100;
let order2 = pool.try_get().unwrap();
assert_eq!(pool.available(), 0);
assert!(pool.try_get().is_none());
drop(order1);
assert_eq!(pool.available(), 1);
let mut order3 = pool.try_get().unwrap();
assert_eq!(pool.available(), 0);
assert_eq!(order3.id, 100); order3.id = 0;
drop(order2);
drop(order3);
assert_eq!(pool.available(), 2);
}
#[test]
fn test_multithreaded_pool() {
let pool = ObjectPool::new(100, new_order);
let num_threads = 10;
let items_per_thread = 50;
let mut handles = vec![];
for _ in 0..num_threads {
let pool_clone = pool.clone();
handles.push(thread::spawn(move || {
for i in 0..items_per_thread {
let mut item = pool_clone.try_get().unwrap();
item.id = i as u64;
}
}));
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(pool.available(), 100);
}
}