gioser
⎇ main crates/ente-kernel/src/sigchld.rs Rust · 66 siq'ikuna · 2.2 KB Ñawpa willay Pi imata llamkarqan ch'uya

Pausa: 11 crates del fractal Ente #0 con cerebro completo d6b8f18b4 · Sergio ·

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}