Pausa: 11 crates del fractal Ente #0 con cerebro completo
1//! SIGCHLD vía signalfd, no signal handler.
2//!
3//! Los handlers async-signal sólo pueden invocar funciones async-signal-safe
4//! — no allocator, no `mpsc::send`. Con signalfd la señal entra al runtime de
5//! Tokio como un `fd` legible y la cosechamos en el bucle como cualquier otro
6//! evento. Esto es lo que hace que un init en Rust moderno sea sano.
7
8use anyhow::Context;
9use nix::sys::signal::Signal;
10use nix::sys::signalfd::{SfdFlags, SigSet, SignalFd};
11use std::os::fd::AsRawFd;
12use tokio::io::unix::AsyncFd;
13use tokio::sync::mpsc;
14use tracing::{error, trace};
15
16/// Bloquea SIGCHLD para entrega asíncrona, abre signalfd, y emite un `()`
17/// en el canal cada vez que llega al menos una señal.
18pub fn spawn_sigchld_stream() -> anyhow::Result<mpsc::Receiver<()>> {
19 let mut mask = SigSet::empty();
20 mask.add(Signal::SIGCHLD);
21 mask.thread_block().context("SIGCHLD thread_block")?;
22
23 let sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK | SfdFlags::SFD_CLOEXEC)
24 .context("signalfd creation")?;
25
26 let async_fd = AsyncFd::new(SignalFdHandle(sfd)).context("AsyncFd::new")?;
27
28 let (tx, rx) = mpsc::channel(8);
29 tokio::spawn(async move {
30 loop {
31 let mut guard = match async_fd.readable().await {
32 Ok(g) => g,
33 Err(e) => { error!(?e, "signalfd readable failed"); return; }
34 };
35 // Drenamos todas las siginfos pendientes; signalfd las coalesce
36 // pero no las cuenta — un read por evento legible es suficiente.
37 drain(guard.get_inner());
38 guard.clear_ready();
39 if tx.send(()).await.is_err() { return; }
40 trace!("SIGCHLD batch coalesced");
41 }
42 });
43
44 Ok(rx)
45}
46
47struct SignalFdHandle(SignalFd);
48
49impl AsRawFd for SignalFdHandle {
50 fn as_raw_fd(&self) -> std::os::fd::RawFd {
51 self.0.as_raw_fd()
52 }
53}
54
55fn drain(handle: &SignalFdHandle) {
56 let fd = handle.as_raw_fd();
57 // Tamaño exacto de signalfd_siginfo. Leemos en bucle hasta EAGAIN.
58 let mut buf = [0u8; std::mem::size_of::<libc::signalfd_siginfo>()];
59 loop {
60 let n = unsafe {
61 libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len())
62 };
63 if n < 0 { return; }
64 if n == 0 { return; }
65 }
66}