use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Condvar, Mutex};
use crate::connection::{Config, Connection};
use crate::error::{Error, Result};
struct Inner {
idle: Vec<Connection>,
open: usize,
}
#[derive(Clone)]
pub struct Pool {
config: Arc<Config>,
max_size: usize,
state: Arc<(Mutex<Inner>, Condvar)>,
}
impl Pool {
pub fn new(config: Config, max_size: usize) -> Result<Self> {
let max_size = max_size.max(1);
Ok(Self {
config: Arc::new(config),
max_size,
state: Arc::new((
Mutex::new(Inner {
idle: Vec::with_capacity(max_size),
open: 0,
}),
Condvar::new(),
)),
})
}
pub fn get(&self) -> Result<PooledConnection> {
let (lock, cvar) = &*self.state;
let mut inner = lock
.lock()
.map_err(|_| Error::Pool("pool mutex poisoned".to_owned()))?;
loop {
if let Some(conn) = inner.idle.pop() {
return Ok(PooledConnection {
conn: Some(conn),
pool: self.clone(),
});
}
if inner.open < self.max_size {
inner.open += 1;
drop(inner);
let conn = match Connection::connect_config(&self.config) {
Ok(c) => c,
Err(e) => {
if let Ok(mut g) = lock.lock() {
g.open -= 1;
cvar.notify_one();
}
return Err(e);
}
};
return Ok(PooledConnection {
conn: Some(conn),
pool: self.clone(),
});
}
inner = cvar
.wait(inner)
.map_err(|_| Error::Pool("pool mutex poisoned".to_owned()))?;
}
}
#[must_use]
pub fn max_size(&self) -> usize {
self.max_size
}
fn put_back(&self, conn: Connection) {
let (lock, cvar) = &*self.state;
if let Ok(mut inner) = lock.lock() {
inner.idle.push(conn);
cvar.notify_one();
}
}
fn discard(&self) {
let (lock, cvar) = &*self.state;
if let Ok(mut inner) = lock.lock() {
inner.open = inner.open.saturating_sub(1);
cvar.notify_one();
}
}
}
pub struct PooledConnection {
conn: Option<Connection>,
pool: Pool,
}
impl PooledConnection {
pub fn discard(mut self) {
self.conn.take();
self.pool.discard();
}
}
impl Deref for PooledConnection {
type Target = Connection;
fn deref(&self) -> &Connection {
self.conn.as_ref().expect("connection present until drop")
}
}
impl DerefMut for PooledConnection {
fn deref_mut(&mut self) -> &mut Connection {
self.conn.as_mut().expect("connection present until drop")
}
}
impl Drop for PooledConnection {
fn drop(&mut self) {
if let Some(conn) = self.conn.take() {
self.pool.put_back(conn);
}
}
}