code wiki / (root) / nx_ts_drain_lib.nx

nx_ts_drain_lib.nx

buildroot/runtime/nx_ts_drain_lib.nx

10029 B199 linesdepth 4pulls 5 transitivereach 5 importersview sourcekind librarytopic ts
docsdependenciesstructsconstsfunctions

about

nx_ts_drain_lib.nx -- TS2: COOPERATIVE DRAIN ON TERM, with a DERIVED deadline. /compare/trafficsafety rung TS2. Accept rule taken VERBATIM from trafficsafety.plan and not re-invented here: "On TERM the daemon STOPS ACCEPTING and KEEPS SERVING in-flight requests until a declared deadline, then exits; the deadline is a named constant, never a literal. ACCEPT RULE: with a request in flight at the moment TERM lands, the client receives a complete response and not a 5xx or a reset, and a daemon that exits early FAILS the gate." THE PROCESS MUST KEEP SERVING AFTER TERM. Kubernetes states the race precisely: endpoint removal happens AT THE SAME TIME AS shutdown begins, not before it, so a process that exits on TERM is exactly what produces the client-visible 5xx. AWS names the cost from the other side: a target that terminates the connection before the deregistration delay elapses makes the client receive a 500-level error. WHY THIS BLOCKS THE SIGNAL INSTEAD OF INSTALLING A HANDLER, and it is not a style choice: (1) A handler that RETURNS needs SA_RESTORER on x86-64 or the return frame is undefined. nx_syscalls.nx's own sys_ignore_sigpipe comment records that it may skip SA_RESTORER ONLY because SIG_IGN never delivers a handler frame -- a drain handler must return, so that exemption does not extend to it. (2) A delivered signal interrupts a blocking syscall with EINTR. A read or write cut mid-response IS the truncated reply this rung exists to prevent, so the mechanism that notices TERM must not be the mechanism that can corrupt the in-flight response. Blocking TERM gives both properties for free: the signal can never kill this process, and it can never interrupt a syscall, so the in-flight request completes on its own natural path. The signal is then consumed as a FILE DESCRIPTOR READ, which is pollable -- so an idle daemon exits the instant TERM arrives rather than sleeping out its budget. SYSCALL NUMBERING, MEASURED FROM THE ARTIFACT RATHER THAN RECALLED. TARGET_X86_64 is hard-pinned undefined, so the x86 backend emits through x86ctx_rv64_to_x86_64_syscall (nx_x86_64_ctx.nx:1916) whose default branch is `return num` -- an unmapped number is NOT an error, it is a DIFFERENT syscall. That table was read in full: 14, 289 and 13 are NOT keys, so a raw x86-64 number passes through UNCHANGED and lands on the intended call. This is the same convention the proven code in this tree already uses -- SYS_SENDMSG=46 (x86 sendmsg, proven by nx_scm_rights_gate 14/14) and the literal 13 in sys_ignore_sigpipe (x86 rt_sigaction, proven by nx_sigpipe_gate). The RV64 spellings would be WRONG here for the same reason: rv64 135 is not a key either, so it would land on x86 personality, and rv64 134 would land on x86 uselib -- which is why nx_signal.nx's nx_signal_action_install cannot work on this target and why this rung does not live in that file. nx_ts_drain_gate proves the number empirically: a child that survives TERM can only have blocked it. license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 2 imports · 3 importers

nx_syscalls.nx nx_resmon_lib.nx nx_ts_drain_lib.nx nx_survey_daemon.nx nx_ts_drain_gate.nx nx_ts_slot_lib.nx

imports: nx_syscalls.nxnx_resmon_lib.nx

imported by: nx_survey_daemon.nxnx_ts_drain_gate.nxnx_ts_slot_lib.nx

structs

none

consts

44const TSD_SIGTERM: i64 = 15
48const TSD_SYS_RT_SIGPROCMASK: i64 = 14
49const TSD_SYS_SIGNALFD4: i64 = 289
51const TSD_SIG_BLOCK: i64 = 0
52const TSD_SFD_NONBLOCK: i64 = 0x800
53const TSD_SFD_CLOEXEC: i64 = 0x80000
54const TSD_SIGSET_BYTES: i64 = 8
55const TSD_SSI_BYTES: i64 = 128
56const TSD_SIGNALFD_NEW: i64 = 0 - 1
58const TSD_POLLFD_BYTES: i64 = 8
59const TSD_POLLIN: i64 = 1
60const TSD_PF_EV_OFF: i64 = 4
61const TSD_PF_RE_OFF: i64 = 6
62const TSD_NFDS_BOTH: i64 = 2
63const TSD_NFDS_ONE: i64 = 1
64const TSD_BYTE_MASK: i64 = 0xff
68const TSD_FD_BYTES: i64 = 4
69const TSD_I16_BYTES: i64 = 2
70const TSD_BITS_PER_BYTE: i64 = 8
72const TSD_CONF: *u8 = "knowledge/trafficsafety_drain.conf" as *u8
73const TSD_KEY: *u8 = "drain_deadline_s" as *u8
74const TSD_CONF_ABSENT: i64 = 0 - 1
78const TSD_OK: i64 = 0
79const TSD_ERR_BLOCK: i64 = 1
80const TSD_ERR_FD: i64 = 2
83const TSD_W_DRAIN: i64 = 1
84const TSD_W_CONN: i64 = 0
85const TSD_W_NONE: i64 = 0 - 1
86const TSD_WAIT_BLOCK: i64 = 0 - 1
89const TSD_SRC_DERIVED: i64 = 0
90const TSD_SRC_CONF: i64 = 1

functions

104func ts_drain_deadline_s(out_src: *i64) -> i64
123func ts_drain_on_term(out_verdict: *i64) -> i64
139func ts_drain_requested(drain_fd: i64) -> i64
147func tsd_pollfd_set(p: *u8, idx: i64, fd: i64) -> i64
called by 1: ts_drain_wait
164func tsd_pollfd_ready(p: *u8, idx: i64) -> i64
called by 1: ts_drain_wait
183func ts_drain_wait(listen_fd: i64, drain_fd: i64, timeout_ms: i64) -> i64