use std::io;
use std::sync::{
Arc, Condvar, Mutex,
atomic::{AtomicUsize, Ordering},
};
pub(super) struct Clients {
pub(super) count: AtomicUsize,
changed: Mutex<()>,
idle: Condvar,
}
impl Clients {
pub(super) const fn new(active: usize) -> Self {
Self {
count: AtomicUsize::new(active),
changed: Mutex::new(()),
idle: Condvar::new(),
}
}
pub(super) fn drain(&self) -> io::Result<()> {
let mut guard = self
.changed
.lock()
.map_err(|error| io::Error::other(format!("client_drain_failed: {error}")))?;
let count = self.count.load(Ordering::Acquire);
if count > 0 {
eprintln!("serve_draining_clients: {count} before stdin shutdown");
}
while self.count.load(Ordering::Acquire) != 0 {
guard = self
.idle
.wait(guard)
.map_err(|error| io::Error::other(format!("client_drain_failed: {error}")))?;
}
drop(guard);
Ok(())
}
}
pub(super) struct Permit(pub(super) Arc<Clients>);
impl Drop for Permit {
fn drop(&mut self) {
let guard = match self.0.changed.lock() {
Ok(guard) => guard,
Err(poisoned) => {
eprintln!("client_drain_failed: {poisoned}");
poisoned.into_inner()
}
};
self.0.count.fetch_sub(1, Ordering::AcqRel);
drop(guard);
self.0.idle.notify_all();
}
}