use std::sync::Arc;
use super::{IoOp, Shard, Shared};
use crate::process::exit::Exit;
use crate::rt::sources::{CHILD, Tokens};
pub(crate) struct Watched {
exit: Arc<Exit>,
#[cfg(port)]
packet: Packet,
}
#[cfg(port)]
struct Packet(crate::syscore::windows::HANDLE);
#[cfg(port)]
unsafe impl Send for Packet {}
pub(crate) type Children = Tokens<Watched, CHILD>;
impl Shard {
pub(crate) fn watch(&self, exit: Arc<Exit>) {
#[cfg(ring)]
if let Some(ring) = self.ring() {
ring.watch_exit(exit);
return;
}
let inserted = self.children.borrow_mut().insert(Watched {
exit: Arc::clone(&exit),
#[cfg(port)]
packet: Packet(core::ptr::null_mut()),
});
let token = match inserted {
Ok(token) => token,
Err(err) => return exit.finish(Err(errno(&err))),
};
match self.register_exit(&exit, token) {
Ok(false) => self.exit_came(token),
Ok(true) => {}
Err(err) => {
self.children.borrow_mut().remove(token);
exit.finish(Err(errno(&err)));
}
}
}
fn register_exit(&self, exit: &Exit, token: u64) -> std::io::Result<bool> {
#[cfg(any(target_os = "linux", target_os = "android"))]
{
let (Some(registrar), Some(pidfd)) = (self.shared.registrar(), exit.pidfd()) else {
return Err(std::io::ErrorKind::Unsupported.into());
};
registrar.add_exit(pidfd, token).map(|()| true)
}
#[cfg(all(readiness, not(any(target_os = "linux", target_os = "android"))))]
{
let Some(registrar) = self.shared.registrar() else {
return Err(std::io::ErrorKind::Unsupported.into());
};
registrar.add_exit(exit.pid(), token)
}
#[cfg(port)]
{
let packet = self.shared.port().watch_exit(exit.process(), token)?;
if let Some(watched) = self.children.borrow_mut().get_mut(token) {
watched.packet = Packet(packet);
}
Ok(true)
}
}
pub(super) fn exit_came(&self, token: u64) {
let Some(watched) = self.children.borrow_mut().remove(token) else {
return;
};
let exit = watched.exit;
#[cfg(any(target_os = "linux", target_os = "android"))]
let outcome = {
if let (Some(registrar), Some(pidfd)) = (self.shared.registrar(), exit.pidfd()) {
let _ = registrar.delete(pidfd, false);
}
exit.reap_pidfd()
};
#[cfg(all(readiness, not(any(target_os = "linux", target_os = "android"))))]
let outcome = exit.reap_by_pid();
#[cfg(port)]
let outcome = {
crate::rt::park::iocp::Port::close_packet(watched.packet.0);
crate::process::exit::windows_status(exit.process())
.map_err(|err| errno(&err))
.transpose()
};
match outcome {
Some(outcome) => exit.finish(outcome),
None => self.watch(exit),
}
}
pub(super) fn let_go_of_children(&self) {
#[cfg(ring)]
if let Some(ring) = self.ring() {
if ring.in_flight() != 0 {
ring.let_go_of_exits();
}
return;
}
if self.children.borrow().is_untouched() {
return;
}
let gone: Vec<Watched> = self.children.borrow_mut().drain().collect();
for watched in gone {
#[cfg(any(target_os = "linux", target_os = "android"))]
if let (Some(registrar), Some(pidfd)) = (self.shared.registrar(), watched.exit.pidfd())
{
let _ = registrar.delete(pidfd, false);
}
#[cfg(all(readiness, not(any(target_os = "linux", target_os = "android"))))]
if let Some(registrar) = self.shared.registrar() {
registrar.delete_exit(watched.exit.pid());
}
#[cfg(port)]
crate::rt::park::iocp::Port::unwatch_exit(watched.packet.0);
let orphan = Arc::strong_count(&watched.exit) == 1;
watched.exit.unwatched(orphan);
}
}
}
fn errno(err: &std::io::Error) -> i32 {
err.raw_os_error().unwrap_or(0)
}
pub(crate) fn watch_exit(exit: &Arc<Exit>) {
let here = crate::rt::context::current(|current| {
current.map(|shard| shard.watch(Arc::clone(exit))).is_some()
});
if !here {
crate::rt::runtime::service_shard().post_io(IoOp::Watch(Arc::clone(exit)));
}
}
pub(crate) fn watch_exit_elsewhere(exit: &Arc<Exit>) -> bool {
match crate::rt::runtime::running_service_shard() {
Some(shard) => {
Shared::post_io(&shard, IoOp::Watch(Arc::clone(exit)));
true
}
None => false,
}
}