nx_daemon_reap_lib.nx
buildroot/runtime/nx_daemon_reap_lib.nx
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
imports: nx_syscalls.nx
imported by: nx_daemon_reap_gate.nx
structs
| none |
consts
| 33 | const DR_WNOHANG: i64 = 1 |
| 34 | const DR_BLOCKING: i64 = 0 |
| 38 | const DR_ANY_CHILD: i64 = 0 - 1 |
functions
| 50 | func dr_reap_nonblocking(status: *i64) -> i64 |
| 60 | func dr_reap_one(status: *i64) -> i64 |
| 69 | func dr_drain(status: *i64, live: *i64) -> i64 |
| 79 | func dr_throttle(status: *i64, live: *i64, cap_children: i64) -> i64 |