use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use tokio::sync::Notify;
#[derive(Clone, Debug)]
pub struct ShutdownConfig {
inner: Arc<ShutdownInner>,
}
struct ShutdownInner {
max_connections: usize,
draining: AtomicBool,
drain_notify: Notify,
}
impl std::fmt::Debug for ShutdownInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ShutdownInner")
.field("max_connections", &self.max_connections)
.field("draining", &self.draining.load(Ordering::Relaxed))
.finish()
}
}
impl ShutdownConfig {
#[must_use]
pub fn new(max_connections: usize) -> Self {
Self {
inner: Arc::new(ShutdownInner {
max_connections,
draining: AtomicBool::new(false),
drain_notify: Notify::new(),
}),
}
}
pub fn begin_drain(&self) {
if !self.inner.draining.swap(true, Ordering::Relaxed) {
self.inner.drain_notify.notify_waiters();
}
}
#[must_use]
pub fn is_draining(&self) -> bool {
self.inner.draining.load(Ordering::Relaxed)
}
#[must_use]
pub fn max_connections(&self) -> usize {
self.inner.max_connections
}
pub fn drain_notified(&self) -> impl std::future::Future<Output = ()> + '_ {
let notify = self.inner.drain_notify.notified();
async move {
if self.is_draining() {
return;
}
notify.await;
}
}
}