use std::{
future::Future,
result::Result as StdResult,
sync::{
Arc,
atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering},
},
time::{Duration, Instant},
};
use crossbeam_queue::ArrayQueue;
use tokio::{
sync::{Semaphore, SemaphorePermit},
task::yield_now,
time::timeout,
};
use super::connection::{Floating, Idle, Live};
use crate::{Error, Result};
fn deadline_as_timeout(deadline: Instant) -> Result<Duration> {
deadline
.checked_duration_since(Instant::now())
.ok_or(Error::PoolTimedOut)
}
pub struct PoolInner {
idle_conns: ArrayQueue<Idle>,
semaphore: Semaphore,
size: AtomicU32,
num_idle: AtomicUsize,
is_closed: AtomicBool,
on_closed: event_listener::Event,
pub(super) options: crate::Musq,
}
impl PoolInner {
pub(super) fn new_arc(options: crate::Musq) -> Arc<Self> {
Arc::new(Self {
idle_conns: ArrayQueue::new(options.pool_max_connections as usize),
semaphore: Semaphore::new(options.pool_max_connections as usize),
size: AtomicU32::new(0),
num_idle: AtomicUsize::new(0),
is_closed: AtomicBool::new(false),
on_closed: event_listener::Event::new(),
options,
})
}
pub(super) fn size(&self) -> u32 {
self.size.load(Ordering::Acquire)
}
pub(super) fn num_idle(&self) -> usize {
self.num_idle.load(Ordering::Acquire)
}
pub(super) fn is_closed(&self) -> bool {
self.is_closed.load(Ordering::Acquire)
}
fn mark_closed(&self) {
self.is_closed.store(true, Ordering::Release);
self.on_closed.notify(usize::MAX);
}
pub(super) async fn close(self: &Arc<Self>) {
self.mark_closed();
loop {
self.close_idle_connections().await;
let size = self.size();
if size == 0 {
return;
}
let permits = self
.options
.pool_max_connections
.saturating_sub(size)
.saturating_add(1);
if let Err(err) = self.semaphore.acquire_many(permits).await {
tracing::warn!("semaphore closed while waiting for permits: {err:?}");
return;
}
}
}
async fn close_idle_connections(&self) {
while let Some(idle) = self.idle_conns.pop() {
self.num_idle.fetch_sub(1, Ordering::AcqRel);
idle.close().await;
self.size.fetch_sub(1, Ordering::AcqRel);
}
}
pub(crate) fn close_event(&self) -> impl Future<Output = ()> + '_ {
let listener = (!self.is_closed()).then(|| self.on_closed.listen());
async move {
if let Some(listener) = listener {
listener.await;
}
}
}
async fn acquire_permit<'a>(self: &'a Arc<Self>) -> Result<SemaphorePermit<'a>> {
tokio::select! {
permit = self.semaphore.acquire_many(1) => {
match permit {
Ok(permit) => Ok(permit),
Err(_) => Err(Error::PoolClosed),
}
}
_ = self.close_event() => Err(Error::PoolClosed),
}
}
pub(super) fn try_acquire(self: &Arc<Self>) -> Option<Floating<Idle>> {
if self.is_closed() {
return None;
}
let permit = self.semaphore.try_acquire_many(1).ok()?;
self.pop_idle(permit).ok()
}
fn pop_idle<'a>(
self: &'a Arc<Self>,
permit: SemaphorePermit<'a>,
) -> StdResult<Floating<Idle>, SemaphorePermit<'a>> {
if let Some(idle) = self.idle_conns.pop() {
self.num_idle.fetch_sub(1, Ordering::AcqRel);
Ok(Floating::from_idle(idle, (*self).clone(), permit))
} else {
Err(permit)
}
}
pub(super) fn release(&self, floating: Floating<Live>) {
let Floating { inner: idle, guard } = floating.into_idle();
if self.idle_conns.push(idle).is_err() {
panic!("BUG: connection queue overflow in release()");
}
guard.release_permit();
self.num_idle.fetch_add(1, Ordering::AcqRel);
}
fn try_increment_size<'a>(
self: &'a Arc<Self>,
permit: SemaphorePermit<'a>,
) -> StdResult<DecrementSizeGuard, SemaphorePermit<'a>> {
match self
.size
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |size| {
if self.is_closed() {
return None;
}
size.checked_add(1)
.filter(|size| size <= &self.options.pool_max_connections)
}) {
Ok(_) => Ok(DecrementSizeGuard::from_permit((*self).clone(), permit)),
Err(_) => Err(permit),
}
}
pub(super) async fn acquire(self: &Arc<Self>) -> Result<Floating<Live>> {
if self.is_closed() {
return Err(Error::PoolClosed);
}
let deadline = Instant::now() + self.options.pool_acquire_timeout;
timeout(
self.options.pool_acquire_timeout,
async {
loop {
let permit = self.acquire_permit().await?;
let guard = match self.pop_idle(permit) {
Ok(conn) => return Ok(conn.into_live()),
Err(permit) => if let Ok(guard) = self.try_increment_size(permit) {
guard
} else {
tracing::debug!("woke but was unable to acquire idle connection or open new one; retrying");
yield_now().await;
continue;
}
};
return self.connect(deadline, guard).await;
}
}
)
.await
.map_err(|_| Error::PoolTimedOut)?
}
async fn connect(
self: &Arc<Self>,
deadline: Instant,
guard: DecrementSizeGuard,
) -> Result<Floating<Live>> {
if self.is_closed() {
return Err(Error::PoolClosed);
}
let timeout_duration = deadline_as_timeout(deadline)?;
match timeout(timeout_duration, self.options.connect()).await {
Ok(Ok(raw)) => Ok(Floating::new_live(raw, guard)),
Ok(Err(e)) => Err(e),
Err(_) => Err(Error::PoolTimedOut),
}
}
}
impl Drop for PoolInner {
fn drop(&mut self) {
self.mark_closed();
}
}
pub(in crate::pool) struct DecrementSizeGuard {
pub(crate) pool: Arc<PoolInner>,
cancelled: bool,
}
impl DecrementSizeGuard {
pub fn new_permit(pool: Arc<PoolInner>) -> Self {
Self {
pool,
cancelled: false,
}
}
pub fn from_permit(pool: Arc<PoolInner>, permit: SemaphorePermit<'_>) -> Self {
permit.forget();
Self::new_permit(pool)
}
fn release_permit(self) {
self.pool.semaphore.add_permits(1);
self.cancel();
}
pub fn cancel(mut self) {
self.cancelled = true;
}
}
impl Drop for DecrementSizeGuard {
fn drop(&mut self) {
if !self.cancelled {
self.pool.size.fetch_sub(1, Ordering::AcqRel);
self.pool.semaphore.add_permits(1);
}
}
}