use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use tokio::sync::Notify;
use super::error::RealtimeError;
#[derive(Clone, Debug, Default)]
pub struct Registry {
inner: Arc<RegistryInner>,
}
#[derive(Debug)]
struct RegistryInner {
live: AtomicUsize,
drain_signal: Notify,
}
impl Default for RegistryInner {
fn default() -> Self {
Self {
live: AtomicUsize::new(0),
drain_signal: Notify::new(),
}
}
}
impl Registry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn live_count(&self) -> usize {
self.inner.live.load(Ordering::Relaxed)
}
pub fn acquire(&self, max: usize) -> Result<ConnectionGuard, RealtimeError> {
loop {
let current = self.inner.live.load(Ordering::Relaxed);
if current >= max {
return Err(RealtimeError::ConnectionLimit);
}
if self
.inner
.live
.compare_exchange_weak(current, current + 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
break;
}
}
Ok(ConnectionGuard {
inner: self.inner.clone(),
})
}
pub async fn drain(&self, bound: Duration) -> Result<(), RealtimeError> {
if self.live_count() == 0 {
return Ok(());
}
let _ = tokio::time::timeout(bound, self.wait_for_zero()).await;
let remaining = self.live_count();
if remaining > 0 {
Err(RealtimeError::Shutdown { remaining })
} else {
Ok(())
}
}
async fn wait_for_zero(&self) {
loop {
if self.live_count() == 0 {
return;
}
let notify = self.inner.drain_signal.notified();
tokio::pin!(notify);
tokio::select! {
_ = &mut notify => {}
_ = tokio::time::sleep(Duration::from_millis(100)) => {}
}
}
}
}
#[derive(Debug)]
pub struct ConnectionGuard {
inner: Arc<RegistryInner>,
}
impl ConnectionGuard {
#[must_use]
pub fn live_count(&self) -> usize {
self.inner.live.load(Ordering::Relaxed)
}
}
impl Drop for ConnectionGuard {
fn drop(&mut self) {
loop {
let current = self.inner.live.load(Ordering::Relaxed);
if current == 0 {
break;
}
if self
.inner
.live
.compare_exchange_weak(current, current - 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
self.inner.drain_signal.notify_one();
break;
}
}
}
}