use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::task::{Context, Poll, Waker};
use std::future::Future;
use std::pin::Pin;
use futures::executor::block_on;
use futures::future::join_all;
use tracing::{info, warn};
use super::server::Server;
static TERMINATING: AtomicBool = AtomicBool::new(false);
static WAKERS: Mutex<Vec<(u64, Waker)>> = Mutex::new(Vec::new());
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
static SERVERS: Mutex<Vec<Weak<Server>>> = Mutex::new(Vec::new());
pub fn terminate() {
TERMINATING.store(true, Ordering::SeqCst);
for (_, waker) in WAKERS.lock().unwrap().drain(..) {
waker.wake();
}
let servers = SERVERS.lock().unwrap().iter()
.filter_map(Weak::upgrade)
.collect::<Vec<_>>();
info!(servers = servers.len(), "Terminating the servers of the process");
let stopped = block_on(join_all(servers.iter().map(|server| server.stop_async())));
for (server, stopped) in servers.iter().zip(stopped) {
if let Err(err) = stopped {
warn!(server = %server.name(), %err, "Failed to stop the server on termination");
}
}
}
pub(super) fn terminating() -> Terminating {
Terminating { id: NEXT_ID.fetch_add(1, Ordering::Relaxed) }
}
pub(super) struct Terminating {
id: u64,
}
impl Future for Terminating {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
let mut wakers = WAKERS.lock().unwrap();
if TERMINATING.load(Ordering::SeqCst) {
return Poll::Ready(());
}
match wakers.iter_mut().find(|(id, _)| *id == self.id) {
Some((_, waker)) => waker.clone_from(cx.waker()),
None => wakers.push((self.id, cx.waker().clone())),
}
Poll::Pending
}
}
impl Drop for Terminating {
fn drop(&mut self) {
WAKERS.lock().unwrap().retain(|(id, _)| *id != self.id);
}
}
pub(super) fn register(server: &Arc<Server>) -> bool {
let mut servers = SERVERS.lock().unwrap();
servers.retain(|server| server.strong_count() > 0);
servers.push(Arc::downgrade(server));
drop(servers);
TERMINATING.load(Ordering::SeqCst)
}