use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use fred::clients::{Client, Pool};
use fred::interfaces::{ClientInterface, ClientLike};
use fred::types::ClientUnblockFlag;
use ruststream::AckError;
use tokio::runtime::Handle;
use tokio::task::JoinHandle;
use crate::error::RedisError;
pub(crate) struct Connection {
pool: Pool,
closed: AtomicBool,
}
impl Connection {
pub(crate) fn new(pool: Pool) -> Arc<Self> {
Arc::new(Self {
pool,
closed: AtomicBool::new(false),
})
}
pub(crate) fn live(&self) -> Result<&Pool, RedisError> {
self.ensure_open()?;
Ok(&self.pool)
}
#[inline]
pub(crate) fn ensure_open(&self) -> Result<(), RedisError> {
if self.closed.load(Ordering::Acquire) {
return Err(shut_down());
}
Ok(())
}
#[inline]
pub(crate) fn ensure_open_to_settle(&self) -> Result<(), AckError> {
if self.closed.load(Ordering::Acquire) {
return Err(AckError::Broker(Box::new(shut_down())));
}
Ok(())
}
pub(crate) const fn pool(&self) -> &Pool {
&self.pool
}
pub(crate) fn close(&self) {
self.closed.store(true, Ordering::Release);
}
pub(crate) fn is_closed(&self) -> bool {
self.closed.load(Ordering::Relaxed)
}
}
#[cold]
const fn shut_down() -> RedisError {
RedisError::ShutDown
}
pub(crate) struct ReadConnection {
client: Client,
runtime: Handle,
closing: Arc<Closing>,
blocks: bool,
}
impl ReadConnection {
pub(crate) const fn new(
client: Client,
runtime: Handle,
closing: Arc<Closing>,
blocks: bool,
) -> Self {
Self {
client,
runtime,
closing,
blocks,
}
}
pub(crate) const fn client(&self) -> &Client {
&self.client
}
}
impl Drop for ReadConnection {
fn drop(&mut self) {
let client = self.client.clone();
let blocks = self.blocks;
let close = self.runtime.spawn(async move {
if blocks {
let _ = client.unblock_self(Some(ClientUnblockFlag::Timeout)).await;
}
let _ = client.quit().await;
});
self.closing.track(close);
}
}
#[derive(Debug, Default)]
pub(crate) struct Closing(Mutex<Vec<JoinHandle<()>>>);
impl Closing {
fn track(&self, close: JoinHandle<()>) {
let mut closes = self.0.lock().expect("read connection closes poisoned");
closes.retain(|close| !close.is_finished());
closes.push(close);
}
pub(crate) async fn finish(&self) {
let closes = mem::take(&mut *self.0.lock().expect("read connection closes poisoned"));
for close in closes {
let _ = close.await;
}
}
}