use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
#[derive(Clone)]
pub struct ShutdownHandle {
flag: Arc<AtomicBool>,
mio_waker: Option<Arc<mio::Waker>>,
}
impl ShutdownHandle {
pub(crate) fn new() -> Self {
Self {
flag: Arc::new(AtomicBool::new(false)),
mio_waker: None,
}
}
pub(crate) fn set_mio_waker(&mut self, waker: Arc<mio::Waker>) {
self.mio_waker = Some(waker);
}
pub fn trigger(&self) {
self.flag.store(true, Ordering::Release);
if let Some(w) = &self.mio_waker {
let _ = w.wake();
}
}
pub fn is_shutdown(&self) -> bool {
self.flag.load(Ordering::Acquire)
}
pub(crate) fn flag_ptr(&self) -> Arc<AtomicBool> {
Arc::clone(&self.flag)
}
pub fn signal(&self) -> ShutdownSignal {
ShutdownSignal {
flag: Arc::as_ptr(&self.flag),
}
}
}
pub struct ShutdownSignal {
pub(crate) flag: *const AtomicBool,
}
impl Future for ShutdownSignal {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
if unsafe { &*self.flag }.load(Ordering::Acquire) {
return Poll::Ready(());
}
Poll::Pending
}
}
pub fn install_signal_handlers(flag: &Arc<AtomicBool>, mio_waker: &Arc<mio::Waker>) {
let waker_ref = Arc::clone(mio_waker);
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(flag))
.expect("failed to register SIGTERM handler");
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(flag))
.expect("failed to register SIGINT handler");
unsafe {
signal_hook::low_level::register(signal_hook::consts::SIGTERM, move || {
let _ = waker_ref.wake();
})
.expect("failed to register SIGTERM waker");
}
let waker_ref2 = Arc::clone(mio_waker);
unsafe {
signal_hook::low_level::register(signal_hook::consts::SIGINT, move || {
let _ = waker_ref2.wake();
})
.expect("failed to register SIGINT waker");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn shutdown_handle_trigger() {
let handle = ShutdownHandle::new();
assert!(!handle.is_shutdown());
handle.trigger();
assert!(handle.is_shutdown());
}
#[test]
fn shutdown_signal_resolves_after_trigger() {
use crate::{Runtime, spawn_boxed};
use nexus_rt::WorldBuilder;
use std::cell::Cell;
use std::rc::Rc;
let wb = WorldBuilder::new();
let mut world = wb.build();
let mut rt = Runtime::new(&mut world);
let shutdown = rt.shutdown_handle();
let done = Rc::new(Cell::new(false));
let flag = done.clone();
let sh = shutdown.clone();
rt.block_on(async move {
spawn_boxed(async move {
crate::context::sleep(std::time::Duration::from_millis(50)).await;
sh.trigger();
});
shutdown.signal().await;
flag.set(true);
});
assert!(done.get());
}
}