use crate::{notify_once::NotifyOnce, server::Address};
use std::{
sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
},
time::Duration,
};
use tokio::time::sleep;
#[derive(Clone, Debug, Default)]
pub struct Control {
inner: Arc<ControlInner>,
}
#[derive(Debug, Default)]
struct ControlInner {
conn_count: AtomicUsize,
shutdown: NotifyOnce,
graceful: NotifyOnce,
graceful_dur: Mutex<Option<Duration>>,
conn_end: NotifyOnce,
}
impl Control {
pub fn new() -> Self {
Self::default()
}
pub fn connection_count(&self) -> usize {
self.inner.conn_count.load(Ordering::SeqCst)
}
pub fn shutdown(&self) {
self.inner.shutdown.notify_waiters();
}
pub fn graceful_shutdown(&self, duration: Option<Duration>) {
*self.inner.graceful_dur.lock().unwrap() = duration;
self.inner.graceful.notify_waiters();
}
pub(crate) async fn wait_shutdown(&self) {
self.inner.shutdown.notified().await;
}
pub(crate) async fn wait_graceful_shutdown(&self) {
self.inner.graceful.notified().await;
}
pub(crate) async fn wait_connections_end(&self) {
if self.inner.conn_count.load(Ordering::SeqCst) == 0 {
return;
}
let deadline = *self.inner.graceful_dur.lock().unwrap();
match deadline {
Some(duration) => tokio::select! {
biased;
_ = sleep(duration) => self.shutdown(),
_ = self.inner.conn_end.notified() => (),
},
None => self.inner.conn_end.notified().await,
}
}
pub(crate) fn watcher(&self) -> Watcher {
Watcher::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Handle<A: Address> {
addr: Arc<AddrSlot<A>>,
control: Control,
}
#[derive(Debug)]
struct AddrSlot<A: Address> {
addr: Mutex<Option<A>>,
addr_notify: NotifyOnce,
}
impl<A: Address> Default for AddrSlot<A> {
fn default() -> Self {
Self {
addr: Default::default(),
addr_notify: Default::default(),
}
}
}
impl<A: Address> Default for Handle<A> {
fn default() -> Self {
Self::new()
}
}
impl<A: Address> Handle<A> {
pub fn new() -> Self {
Self::with_control(Control::new())
}
pub fn with_control(control: Control) -> Self {
Self {
addr: Arc::new(AddrSlot::default()),
control,
}
}
pub fn control(&self) -> Control {
self.control.clone()
}
pub fn connection_count(&self) -> usize {
self.control.connection_count()
}
pub fn shutdown(&self) {
self.control.shutdown();
}
pub fn graceful_shutdown(&self, duration: Option<Duration>) {
self.control.graceful_shutdown(duration);
}
pub async fn listening(&self) -> Option<A> {
self.addr.addr_notify.notified().await;
self.addr.addr.lock().unwrap().clone()
}
pub(crate) fn notify_listening(&self, addr: Option<A>) {
*self.addr.addr.lock().unwrap() = addr;
self.addr.addr_notify.notify_waiters();
}
pub(crate) fn watcher(&self) -> Watcher {
self.control.watcher()
}
pub(crate) async fn wait_shutdown(&self) {
self.control.wait_shutdown().await;
}
pub(crate) async fn wait_graceful_shutdown(&self) {
self.control.wait_graceful_shutdown().await;
}
pub(crate) async fn wait_connections_end(&self) {
self.control.wait_connections_end().await;
}
}
pub(crate) struct Watcher {
control: Control,
}
impl Watcher {
fn new(control: Control) -> Self {
control.inner.conn_count.fetch_add(1, Ordering::SeqCst);
Self { control }
}
pub(crate) async fn wait_graceful_shutdown(&self) {
self.control.wait_graceful_shutdown().await
}
pub(crate) async fn wait_shutdown(&self) {
self.control.wait_shutdown().await
}
}
impl Drop for Watcher {
fn drop(&mut self) {
let count = self.control.inner.conn_count.fetch_sub(1, Ordering::SeqCst) - 1;
if count == 0 && self.control.inner.graceful.is_notified() {
self.control.inner.conn_end.notify_waiters();
}
}
}
#[cfg(test)]
mod tests {
use super::{Control, Handle};
use std::{net::SocketAddr, time::Duration};
#[tokio::test]
async fn listening_returns_none_after_bind_failure() {
let handle = Handle::<SocketAddr>::new();
handle.notify_listening(None);
let addr = tokio::time::timeout(Duration::from_secs(1), handle.listening())
.await
.expect("listening() should resolve immediately after a bind failure");
assert_eq!(addr, None);
}
#[tokio::test]
async fn listening_returns_addr_after_successful_bind() {
let handle = Handle::<SocketAddr>::new();
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
handle.notify_listening(Some(addr));
let resolved = tokio::time::timeout(Duration::from_secs(1), handle.listening())
.await
.expect("listening() should resolve immediately");
assert_eq!(resolved, Some(addr));
}
#[tokio::test]
async fn control_is_shared_between_handles() {
let control = Control::new();
let first = Handle::<SocketAddr>::with_control(control.clone());
let second = Handle::<SocketAddr>::with_control(control.clone());
assert!(std::sync::Arc::ptr_eq(
&first.control.inner,
&second.control.inner
));
assert!(std::sync::Arc::ptr_eq(&control.inner, &first.control.inner));
}
}