use crate::error::IsleError;
use crate::handle::Isle;
use std::cell::Cell;
use std::ops::Deref;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
type Factory = dyn Fn(&mlua::Lua) -> Result<(), mlua::Error> + Send + Sync;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PoolStrategy {
Cold,
Warm,
}
#[derive(Debug, Clone)]
pub struct PoolConfig {
pub max_size: usize,
pub strategy: PoolStrategy,
}
struct PoolInner {
idle: Vec<Isle>,
active: usize,
closed: bool,
}
pub struct IslePool {
inner: Mutex<PoolInner>,
condvar: Condvar,
factory: Arc<Factory>,
config: PoolConfig,
}
impl IslePool {
pub fn new<F>(factory: F, config: PoolConfig) -> Result<Self, IsleError>
where
F: Fn(&mlua::Lua) -> Result<(), mlua::Error> + Send + Sync + 'static,
{
if config.max_size == 0 {
return Err(IsleError::Init("max_size must be > 0".into()));
}
Ok(Self {
inner: Mutex::new(PoolInner {
idle: Vec::with_capacity(config.max_size),
active: 0,
closed: false,
}),
condvar: Condvar::new(),
factory: Arc::new(factory),
config,
})
}
pub fn checkout(&self) -> Result<PooledIsle<'_>, IsleError> {
let mut inner = self.lock_inner()?;
loop {
if inner.closed {
return Err(IsleError::Shutdown);
}
match self.try_acquire(inner)? {
Acquired::Isle(pooled) => return Ok(pooled),
Acquired::NeedWait(guard) => {
inner = self
.condvar
.wait(guard)
.map_err(|e| IsleError::PoolPoisoned(e.to_string()))?;
}
}
}
}
pub fn try_checkout(&self) -> Result<Option<PooledIsle<'_>>, IsleError> {
let inner = self.lock_inner()?;
if inner.closed {
return Err(IsleError::Shutdown);
}
match self.try_acquire(inner)? {
Acquired::Isle(pooled) => Ok(Some(pooled)),
Acquired::NeedWait(_) => Ok(None),
}
}
pub fn checkout_timeout(&self, timeout: Duration) -> Result<PooledIsle<'_>, IsleError> {
let mut inner = self.lock_inner()?;
let deadline = std::time::Instant::now() + timeout;
loop {
if inner.closed {
return Err(IsleError::Shutdown);
}
match self.try_acquire(inner)? {
Acquired::Isle(pooled) => return Ok(pooled),
Acquired::NeedWait(guard) => {
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
if remaining.is_zero() {
return Err(IsleError::PoolExhausted(self.config.max_size));
}
let (guard, _) = self
.condvar
.wait_timeout(guard, remaining)
.map_err(|e| IsleError::PoolPoisoned(e.to_string()))?;
inner = guard;
}
}
}
}
pub fn active(&self) -> usize {
self.inner.lock().map(|g| g.active).unwrap_or(0)
}
pub fn idle(&self) -> usize {
self.inner.lock().map(|g| g.idle.len()).unwrap_or(0)
}
pub fn shutdown(&self) {
let mut inner = match self.inner.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
inner.closed = true;
for isle in inner.idle.drain(..) {
let _ = isle.shutdown();
}
self.condvar.notify_all();
}
fn try_acquire<'a>(
&'a self,
mut inner: std::sync::MutexGuard<'a, PoolInner>,
) -> Result<Acquired<'a>, IsleError> {
if let Some(isle) = self.take_alive_isle(&mut inner) {
inner.active += 1;
return Ok(Acquired::Isle(PooledIsle::new(self, isle)));
}
if self.can_grow(&inner) {
inner.active += 1;
drop(inner);
match self.spawn_isle() {
Ok(isle) => Ok(Acquired::Isle(PooledIsle::new(self, isle))),
Err(e) => {
self.dec_active();
Err(e)
}
}
} else {
Ok(Acquired::NeedWait(inner))
}
}
fn return_isle(&self, isle: Isle) {
let mut inner = match self.inner.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
inner.active = inner.active.saturating_sub(1);
if inner.closed {
let _ = isle.shutdown();
self.condvar.notify_one();
return;
}
match self.config.strategy {
PoolStrategy::Cold => {
let _ = isle.shutdown();
}
PoolStrategy::Warm => {
if isle.is_alive() {
inner.idle.push(isle);
}
}
}
self.condvar.notify_one();
}
fn discard_isle(&self, isle: Isle) {
let _ = isle.shutdown();
let mut inner = match self.inner.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
inner.active = inner.active.saturating_sub(1);
self.condvar.notify_one();
}
fn take_alive_isle(&self, inner: &mut PoolInner) -> Option<Isle> {
while let Some(isle) = inner.idle.pop() {
if isle.is_alive() {
return Some(isle);
}
}
None
}
fn can_grow(&self, inner: &PoolInner) -> bool {
inner.active + inner.idle.len() < self.config.max_size
}
fn spawn_isle(&self) -> Result<Isle, IsleError> {
let factory = Arc::clone(&self.factory);
Isle::spawn(move |lua| factory(lua))
}
fn lock_inner(&self) -> Result<std::sync::MutexGuard<'_, PoolInner>, IsleError> {
self.inner
.lock()
.map_err(|e| IsleError::PoolPoisoned(e.to_string()))
}
fn dec_active(&self) {
let mut inner = match self.inner.lock() {
Ok(g) => g,
Err(e) => e.into_inner(),
};
inner.active = inner.active.saturating_sub(1);
self.condvar.notify_one();
}
}
enum Acquired<'pool> {
Isle(PooledIsle<'pool>),
NeedWait(std::sync::MutexGuard<'pool, PoolInner>),
}
pub struct PooledIsle<'pool> {
pool: &'pool IslePool,
isle: Option<Isle>,
killed: Cell<bool>,
}
impl std::fmt::Debug for PooledIsle<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PooledIsle")
.field("alive", &self.isle.as_ref().map(|i| i.is_alive()))
.field("killed", &self.killed.get())
.finish()
}
}
impl<'pool> PooledIsle<'pool> {
fn new(pool: &'pool IslePool, isle: Isle) -> Self {
Self {
pool,
isle: Some(isle),
killed: Cell::new(false),
}
}
pub fn kill(&self) {
self.killed.set(true);
}
}
impl Deref for PooledIsle<'_> {
type Target = Isle;
fn deref(&self) -> &Isle {
self.isle.as_ref().expect("PooledIsle used after drop")
}
}
impl Drop for PooledIsle<'_> {
fn drop(&mut self) {
if let Some(isle) = self.isle.take() {
if self.killed.get() {
self.pool.discard_isle(isle);
} else {
self.pool.return_isle(isle);
}
}
}
}