use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Mutex;
use std::task::Waker;
use std::time::Duration;
pub const SETTLE: Duration = Duration::from_secs(35);
static IN_FLIGHT: AtomicUsize = AtomicUsize::new(0);
static READER: Mutex<Option<Waker>> = Mutex::new(None);
static CLOSING: AtomicBool = AtomicBool::new(false);
fn close() {
CLOSING.store(true, Ordering::Release);
wake_reader();
}
pub fn closing() -> bool {
CLOSING.load(Ordering::Acquire)
}
#[cfg(target_os = "linux")]
pub fn watch_parent() {
let parent = unsafe { libc::getppid() };
if parent <= 1 {
return;
}
let Ok(mut hangup) = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::hangup())
else {
return;
};
unsafe {
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGHUP);
}
let gone = move || unsafe { libc::getppid() } != parent;
if gone() {
close();
return;
}
tokio::spawn(async move {
while hangup.recv().await.is_some() {
if gone() {
close();
return;
}
}
});
}
#[cfg(target_os = "macos")]
pub fn watch_parent() {
let parent = unsafe { libc::getppid() };
if parent <= 1 {
return;
}
let _ = std::thread::Builder::new()
.name("ljos-mcp-parent".into())
.stack_size(64 * 1024)
.spawn(move || {
if parent_exit(parent) {
close();
}
});
}
#[cfg(target_os = "macos")]
fn parent_exit(parent: libc::pid_t) -> bool {
unsafe {
let queue = libc::kqueue();
if queue < 0 {
return false;
}
let mut change: libc::kevent = std::mem::zeroed();
change.ident = parent as libc::uintptr_t;
change.filter = libc::EVFILT_PROC;
change.flags = libc::EV_ADD | libc::EV_ONESHOT;
change.fflags = libc::NOTE_EXIT;
let exited = loop {
let mut event: libc::kevent = std::mem::zeroed();
let n = libc::kevent(queue, &change, 1, &mut event, 1, std::ptr::null());
let errno = std::io::Error::last_os_error().raw_os_error();
if n < 0 && errno == Some(libc::EINTR) {
continue;
}
if n < 0 {
break errno == Some(libc::ESRCH);
}
if n == 0 {
continue;
}
if event.flags & libc::EV_ERROR != 0 {
break event.data == libc::ESRCH as libc::intptr_t;
}
break event.fflags & libc::NOTE_EXIT != 0;
};
libc::close(queue);
exited
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
pub fn watch_parent() {}
pub struct InFlight(());
impl Drop for InFlight {
fn drop(&mut self) {
if IN_FLIGHT.fetch_sub(1, Ordering::AcqRel) == 1 {
wake_reader();
}
}
}
#[must_use]
pub fn call_in_flight() -> InFlight {
IN_FLIGHT.fetch_add(1, Ordering::AcqRel);
InFlight(())
}
pub fn settled() -> bool {
IN_FLIGHT.load(Ordering::Acquire) == 0
}
pub fn park_reader(waker: &Waker) {
let mut slot = READER.lock().unwrap_or_else(|err| err.into_inner());
match slot.as_ref() {
Some(held) if held.will_wake(waker) => {}
_ => *slot = Some(waker.clone()),
}
}
fn wake_reader() {
let waker = READER.lock().unwrap_or_else(|err| err.into_inner()).take();
if let Some(waker) = waker {
waker.wake();
}
}