#![allow(warnings)]
use std::{
ffi::CString,
sync::{Arc, Condvar, Mutex},
};
use crate::{
Result, SqliteStr,
common::stack::Stack,
sqlite::{OpenFlag, SqliteHandle, error::OpenError},
};
mod shared;
const MAX_POOL_SIZE: usize = 8;
pub struct Pool {
inner: Arc<PoolInner>,
conn: Option<SqliteHandle>,
path: CString,
flags: OpenFlag,
}
struct PoolInner {
pool: Mutex<PoolStack>,
cond: Condvar,
}
struct PoolStack {
stack: Stack<SqliteHandle,MAX_POOL_SIZE>,
alive: usize,
}
impl Pool {
#[allow(unused)]
pub fn setup<S: SqliteStr>(path: S) -> Result<Pool, OpenError> {
Self::setup_with(path, <_>::default())
}
#[allow(unused)]
pub fn setup_with<S: SqliteStr>(path: S, flags: OpenFlag) -> Result<Pool, OpenError> {
Ok(Pool {
path: path.to_nul_string()?.into_owned(),
flags,
conn: None,
inner: Arc::new(PoolInner {
pool: Mutex::new(PoolStack {
stack: Stack::with_size(),
alive: 0,
}),
cond: Condvar::new(),
}),
})
}
#[allow(unused)]
fn checkout(&mut self) -> Result<&mut SqliteHandle, OpenError> {
if self.conn.is_some() {
return Ok(self.conn.as_mut().unwrap())
}
let mut pool = match self.inner.pool.lock() {
Ok(ok) => ok,
Err(err) => err.into_inner(),
};
let conn = loop {
if let Some(ok) = pool.stack.pop() {
break ok
}
if pool.alive == MAX_POOL_SIZE {
pool = match self.inner.cond.wait(pool) {
Ok(ok) => ok,
Err(err) => err.into_inner(),
};
} else {
drop(pool);
let conn = SqliteHandle::open_v2(&self.path, self.flags)?;
pool = match self.inner.pool.lock() {
Ok(ok) => ok,
Err(err) => err.into_inner(),
};
pool.alive += 1;
break conn;
}
};
Ok(self.conn.insert(conn))
}
#[allow(unused)]
fn release(&mut self) {
if self.conn.is_none() {
return;
}
let mut pool = match self.inner.pool.lock() {
Ok(ok) => ok,
Err(err) => err.into_inner(),
};
pool.stack.push(self.conn.take().unwrap());
}
}
impl Drop for Pool {
fn drop(&mut self) {
self.release();
}
}