code wiki / (root) / nx_daemon_reap_lib.nx

nx_daemon_reap_lib.nx

buildroot/runtime/nx_daemon_reap_lib.nx

5536 B85 linesdepth 2pulls 2 transitivereach 1 importersview sourcekind librarytopic daemon
docsdependenciesstructsconstsfunctions

about

nx_daemon_reap_lib.nx -- THE ONE CHILD REAPER for every daemon in the estate that forks. WHY (measured 2026-09-03, filed as debt 1788453613, worklist knowledge/status/daemon_reap_worklist.md): two `ps ... | grep defunct` samples 8 seconds apart on the NAS showed the SAME zombie pids with etimes rising 5s -> 13s. That is a LEAK, not the one-loop lag a reaping daemon shows. Sixteen daemons each forked a child at one instant and none of them waited. The control was in the same data: nx_tools_api_se and sites.elf CHANGED zombie pid between the two samples -- those two reap, and their children rotate. Five more zombies had never been reaped at all, the worst 32.8 DAYS old with ppid 1. AND THE FIX ALREADY EXISTED, IN EXACTLY ONE SIBLING. buildroot/runtime/bin/nx_sites_daemon.nx:532 runs a non-blocking drain on every accept-loop iteration and :540 block-reaps one child at the concurrency cap. It is correct, it is commented, and it was never shared -- so sixteen organs re-derived the same fork pattern without it. *A FIX THAT LIVES IN ONE ORGAN AND NOT ITS SIBLINGS IS HALF A FIX, AND THE MISSING HALF IS INVISIBLE UNTIL SOMETHING COUNTS THE ZOMBIES.* CHECK-BEFORE-BUILD, RECORDED: nx_capsearch over 7,264 organs (corpus_complete=1) for "daemon reap child wait shared lib" returned nx_orphan_reap (kills orphaned gate daemons by cmdline), nx_tmpstore_reap (scratch seg-stores), nx_lock_reap_core (stale LOCKS), nx_toolreg_reap (registry rows) and nx_jobclaim_reap_gate (dead job claims). NOT ONE OF THEM WAITS ON A CHILD PROCESS. This lib is the missing primitive, not a second ruler. WHY A LIB AND NOT SIXTEEN EDITS: when N organs must agree, make disagreement impossible by construction. The WNOHANG-vs-blocking distinction, the -1 (any child) argument and the live-counter arithmetic are exactly the three things sixteen hand-written copies would each get subtly wrong. THE STATUS BUFFER IS THE CALLER'S, ON PURPOSE. A daemon mmaps it ONCE before its accept loop (and before its first fork, so children inherit it copy-on-write: one page for the whole process tree). Allocating inside these functions would put an mmap in the hot loop of every forking daemon in the estate. license_tier: ORIGINAL No hw writes (Rule 26). lib (no main)

dependencies 1 imports · 1 importers

nx_syscalls.nx nx_daemon_reap_lib.nx nx_daemon_reap_gate.nx

imports: nx_syscalls.nx

imported by: nx_daemon_reap_gate.nx

structs

none

consts

33const DR_WNOHANG: i64 = 1
34const DR_BLOCKING: i64 = 0
38const DR_ANY_CHILD: i64 = 0 - 1

functions

50func dr_reap_nonblocking(status: *i64) -> i64
called by 2: maindr_drain calls 1: sys_wait4
60func dr_reap_one(status: *i64) -> i64
called by 2: maindr_throttle calls 1: sys_wait4
69func dr_drain(status: *i64, live: *i64) -> i64
called by 1: main calls 1: dr_reap_nonblocking
79func dr_throttle(status: *i64, live: *i64, cap_children: i64) -> i64
called by 1: main calls 1: dr_reap_one