use std::{
future::Future,
ops::{Deref, DerefMut},
pin::Pin,
sync::Arc,
};
use parking_lot::Mutex;
type Stack<T> = Vec<T>;
type Init<T> =
Box<dyn Fn() -> Pin<Box<dyn Future<Output = T> + Send + 'static>> + Send + Sync + 'static>;
type Reset<T> =
Box<dyn Fn(T) -> Pin<Box<dyn Future<Output = T> + Send + 'static>> + Send + Sync + 'static>;
pub struct ResourcePool<T> {
resources: Arc<Mutex<Stack<T>>>,
init: Arc<Init<T>>,
reset: Arc<Reset<T>>,
}
impl<T> ResourcePool<T> {
pub fn new(init: Init<T>, reset: Reset<T>) -> Self {
Self {
resources: Arc::new(Mutex::new(Stack::new())),
init: Arc::new(init),
reset: Arc::new(reset),
}
}
pub async fn acquire(&self) -> Reusable<T> {
let resource = self.resources.lock().pop();
let resource = if let Some(resource) = resource {
(self.reset)(resource).await
} else {
(self.init)().await
};
Reusable::new(self, resource)
}
pub fn release(&self, t: T) {
self.resources.lock().push(t);
}
pub fn shared(&self) -> Arc<Self> {
Arc::new(Self {
resources: self.resources.clone(),
init: self.init.clone(),
reset: self.reset.clone(),
})
}
}
pub struct Reusable<T> {
pool: Arc<ResourcePool<T>>,
data: Option<T>,
}
impl<T> Reusable<T> {
fn new(pool: &ResourcePool<T>, t: T) -> Self {
Self {
pool: Arc::new(ResourcePool {
resources: pool.resources.clone(),
init: pool.init.clone(),
reset: pool.reset.clone(),
}),
data: Some(t),
}
}
pub fn release(mut self) {
if let Some(data) = self.data.take() {
self.pool.release(data);
}
}
}
const DATA_MUST_CONTAIN_SOME: &str = "data must always contain a [Some] value";
impl<T> Deref for Reusable<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.data.as_ref().expect(DATA_MUST_CONTAIN_SOME)
}
}
impl<T> DerefMut for Reusable<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.data.as_mut().expect(DATA_MUST_CONTAIN_SOME)
}
}
impl<T> Drop for Reusable<T> {
#[inline]
fn drop(&mut self) {
if let Some(data) = self.data.take() {
self.pool.release(data);
}
}
}