use crate::{Error, ErrorKind};
use crate::{Server, ServerOpts};
use std::collections::VecDeque;
use std::ops::{Deref, DerefMut, Drop};
use std::sync::Mutex;
use tokio::sync::{Semaphore, SemaphorePermit};
const DEFAULT_POOL_SIZE: usize = if cfg!(target_os = "macos") { 20 } else { 50 };
pub(crate) static SERVER_POOL: ServerPool = ServerPool::new(DEFAULT_POOL_SIZE);
pub struct ServerGuard {
server: Option<Server>,
_permit: SemaphorePermit<'static>,
}
impl ServerGuard {
fn new(server: Server, _permit: SemaphorePermit<'static>) -> ServerGuard {
ServerGuard {
server: Some(server),
_permit,
}
}
}
impl Deref for ServerGuard {
type Target = Server;
fn deref(&self) -> &Self::Target {
self.server.as_ref().unwrap()
}
}
impl DerefMut for ServerGuard {
fn deref_mut(&mut self) -> &mut Self::Target {
self.server.as_mut().unwrap()
}
}
impl Drop for ServerGuard {
fn drop(&mut self) {
if let Some(server) = self.server.take() {
SERVER_POOL.recycle(server);
}
}
}
pub(crate) struct ServerPool {
semaphore: Semaphore,
free_list: Mutex<VecDeque<Server>>,
}
impl ServerPool {
const fn new(max_size: usize) -> ServerPool {
ServerPool {
semaphore: Semaphore::const_new(max_size),
free_list: Mutex::new(VecDeque::new()),
}
}
pub(crate) async fn get_async(&'static self) -> Result<ServerGuard, Error> {
let permit = self
.semaphore
.acquire()
.await
.map_err(|err| Error::new_with_context(ErrorKind::Deadlock, err))?;
let recycled = self.free_list.lock().unwrap().pop_front();
let server = match recycled {
Some(server) => server,
None => Server::try_new_with_opts_async(ServerOpts::default()).await?,
};
Ok(ServerGuard::new(server, permit))
}
fn recycle(&self, mut server: Server) {
server.reset();
self.free_list.lock().unwrap().push_back(server);
}
}