code wiki / (root) / nx_ts_drain_gate.nx

nx_ts_drain_gate.nx

buildroot/runtime/nx_ts_drain_gate.nx

18005 B395 linesdepth 5pulls 8 transitivereach 0 importersview sourcekind gate/prooftopic ts
docsdependenciesstructsconstsfunctions

about

nx_ts_drain_gate.nx -- TS2: THE PROCESS MUST KEEP SERVING AFTER TERM. /compare/trafficsafety rung TS2, accept rule taken VERBATIM from trafficsafety.plan and not re-invented here: "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. Anti-vacuity tooth required -- a run in which no request was in flight when TERM landed must be reported as UNOBSERVED and must not score as a pass." THE MEASUREMENT IS WHAT THE CLIENT RECEIVED, NOT WHAT THE SERVER LOGGED. A drain that logs "draining" and still cuts the response is the defect, so nothing here reads the subject's output: a real client drives a real TCP connection and the verdict is whether the completion byte arrived. THE IN-FLIGHT WINDOW IS CONSTRUCTED, NOT HOPED FOR, and that is what makes this a gate rather than a race. The subject answers a request in TWO parts: it writes ACK (the response has begun and is therefore incomplete), then BLOCKS reading GO before writing DONE. The parent sends TERM and only THEN sends GO. So at the instant TERM lands the subject is provably mid-request, every time: - drain armed -> TERM is blocked, the blocking read is NOT interrupted, GO arrives, DONE is written. The client gets a complete response AFTER TERM. - neg-control -> TERM is delivered to a process blocked in read, default action TERMINATE, so the subject dies BEFORE GO is ever written and the client gets nothing. The ordering (TERM strictly before GO) is what removes the race; a sleep would have re-introduced it and would have been a magic number besides. TEST THE STATE, NOT THE MESSAGE. The neg-control's tooth asserts the subject died BY SIGNAL 15 -- read out of the wait status, not out of any text -- because "the client got nothing" has several possible causes and only one of them is the one this rung is about. IT ALSO VERIFIES THE SYSCALL NUMBER EMPIRICALLY. A subject that survives TERM can only have blocked it, so T04 is a live check that raw x86-64 14 (rt_sigprocmask) really passes through x86ctx_rv64_to_x86_64_syscall unchanged. Reasoning about that table is not the same as running it. SAFETY: loopback only; every port is FOUND by a bind that would have failed had it been occupied, because a probe port you did not verify free is not a control, it is a second instance. The listening socket is created by the PARENT and inherited across fork, so a child is never racing to bind and the client can connect the instant the subject exists. No serving daemon is touched. expect_exit: 0 license_tier: ORIGINAL No hw writes (Rule 26).

dependencies 4 imports · 0 importers

nx_syscalls.nx nx_ts_drain_lib.nx nx_gate_emit_lib.nx nx_gate_verdict.nx nx_ts_drain_gate.nx

imports: nx_syscalls.nxnx_ts_drain_lib.nxnx_gate_emit_lib.nxnx_gate_verdict.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main gv_ctr sys_mmap nxa_die sys_write sys_exit nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ gv_head gv_puts sys_write ↻ sys_mkdir sys_mmap ↻ ts_drain_deadline_s sys_mmap ↻ sys_read_file sys_openat_rd sys_lseek sys_mmap ↻ sys_read sys_munmap sys_close rm_conf rm_field rm_slen g_puts sys_write ↻ g_putn sys_write ↻ sys_mmap ↻ gq

structs

none

consts

42const TD_NUM_SCRATCH: i64 = 24
43const TD_ASCII_ZERO: i64 = 48
44const TD_B10: i64 = 10
45const TD_ONE: i64 = 1
46const TD_SA_BYTES: i64 = 16
49const TD_SA_PORT_OFF: i64 = 2
50const TD_SA_ADDR_OFF: i64 = 4
51const TD_ADDR_BYTES: i64 = 4
52const TD_BYTE_RADIX: i64 = 256
53const TD_OUT_SLOTS: i64 = 8
54const TD_OUT_BYTES: i64 = 64
55const TD_LOOPBACK_A: i64 = 127
56const TD_LOOPBACK_D: i64 = 1
57const TD_PORT_BASE: i64 = 39300
58const TD_PORT_TRIES: i64 = 64
59const TD_PORT_NONE: i64 = 0 - 1
60const TD_BACKLOG: i64 = 8
61const TD_TRUE: i64 = 1
62const TD_FALSE: i64 = 0
65const TD_PING: i64 = 80
66const TD_ACK: i64 = 65
67const TD_GO: i64 = 71
68const TD_DONE: i64 = 68
69const TD_READY: i64 = 82
73const TD_DEADLINE_S: i64 = ACCEPT_TMO_S
76const TD_C_OK: i64 = 0
77const TD_C_ARM: i64 = 2
78const TD_C_ACCEPT: i64 = 3
79const TD_C_BADREQ: i64 = 4
80const TD_C_WRITE: i64 = 5
81const TD_C_NODRAIN: i64 = 6
82const TD_C_IDLE_DRAIN: i64 = 7
84const TD_SIG_TERM: i64 = 15
85const TD_SIG_NONE: i64 = 0
88const TD_O_ACK: i64 = 0
89const TD_O_DONE: i64 = 1
90const TD_O_EXIT: i64 = 2
91const TD_O_TSIG: i64 = 3
92const TD_O_INFLIGHT: i64 = 4
93const TD_O_POST: i64 = 5
95const TD_DIR: *u8 = "/tmp/nx_ts_drain_gate" as *u8

functions

97func g_putn(v: i64) -> i64
called by 3: gqtd_reportmain calls 2: sys_writesys_mmap
108func gq(label: *u8, got: i64, want: i64, ctr: *i64) -> i64
called by 1: main calls 3: gv_checkg_putsg_putn
120func td_sa(sa: *u8, port: i64) -> i64
called by 2: td_listentd_connect
132func td_put1(fd: i64, v: i64) -> i64
137func td_get1(fd: i64) -> i64
147func td_listen(port: i64, reuse: i64) -> i64
164func td_find_port(from: i64) -> i64
called by 1: main calls 2: td_listensys_close
175func td_connect(port: i64) -> i64
190func td_subject_inflight(armed: i64, lfd: i64) -> i64
222func td_subject_idle(lfd: i64) -> i64
242func td_run_inflight(armed: i64, lfd: i64, port: i64, out: *i64) -> i64
269func td_report(tag: *u8, out: *i64) -> i64
called by 1: main calls 2: g_putsg_putn
280func main() -> i64