code wiki / _hdl_build / nx_ts_handoff_gate.nx

nx_ts_handoff_gate.nx

buildroot/runtime/_hdl_build/nx_ts_handoff_gate.nx

17645 B413 linesdepth 4pulls 6 transitivereach 0 importersview sourcekind gate/prooftopic ts
docsdependenciesstructsconstsfunctions

about

nx_ts_handoff_gate.nx -- TS1: KEEP THE SOCKET, REPLACE THE PROCESS. /compare/trafficsafety rung TS1, accept rule taken VERBATIM from trafficsafety.plan and not re-invented here: "a swap under a synthetic request stream drops ZERO connections, measured by a client that counts refusals and resets, not by the daemon's own log. The neg-control is mandatory -- the same harness against the CURRENT ordinary-listener path must show a non-zero drop, or the test proves nothing." THE MEASUREMENT IS DROPPED CONNECTIONS, NOT BINDERS. nx_hotlisten_gate proves two processes can co-bind a port, which is the NECESSARY condition and never the sufficient one -- LWN documents SO_REUSEPORT as itself dropping connections during the three-way handshake when the listening set changes. So nothing here counts binders. A client process drives real TCP connections, and every connect that is refused and every exchange that does not complete is counted as a DROP. THE DECISIVE TOOTH IS THE ARRIVAL DURING THE GAP. Both arms run the same script: generation one serves a stream, generation one EXITS AND IS REAPED so that zero servers exist, a client connects at that exact instant, and only then is generation two started. With an owner outside the service holding the listener the arrival completes its handshake into the kernel's accept queue and is served microseconds later; with the current bind-per-process path there is nothing bound and the arrival is refused. That difference is deterministic, which is why it can be a gate. SCOPE DECLARED UP FRONT RATHER THAN IMPLIED. This proves the STRUCTURAL property -- an arrival during the swap is not refused. It is NOT a failure-RATE measurement: HAProxy's published figure for the un-fixed race is 155 failures per million connections, and no run of this size could see a rate like that. Measuring that would need a load generator and is a different instrument; the ceiling is stated here rather than left for a reader to assume it was covered. SAFETY: loopback only, and every port is FOUND by a bind that would have failed if the port were occupied -- a probe port you did not verify free is not a control, it is a second instance. 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_http_server.nx nx_gate_emit_lib.nx nx_gate_verdict.nx nx_ts_handoff_gate.nx

imports: nx_syscalls.nxnx_http_server.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_unlinkat th_find_port th_listen sys_socket sys_mmap ↻ sys_setsockopt th_sa sys_bind sys_close sys_listen sys_close ↻ gv_need gv_puts ↻ gv_verdict gv_note_bare_rate gv_bare_rate gv_at gv_obj_has_n gv_at ↻

structs

none

consts

37const TH_NUM_SCRATCH: i64 = 24
38const TH_ASCII_ZERO: i64 = 48
39const TH_B10: i64 = 10
40const TH_ONE_BYTE: i64 = 1
41const TH_SA_IN_BYTES: i64 = 16
42const TH_BYTE_RADIX: i64 = 256
43const TH_OUT_SLOTS: i64 = 8
44const TH_OUT_BYTES: i64 = 64
45const TH_LOOPBACK_A: i64 = 127
46const TH_LOOPBACK_D: i64 = 1
47const TH_PORT_BASE: i64 = 39100
48const TH_PORT_TRIES: i64 = 64
49const TH_PORT_NONE: i64 = 0 - 1
50const TH_PING_BYTE: i64 = 80
51const TH_ECHO_BYTE: i64 = 90
52const TH_READY_BYTE: i64 = 82
53const TH_BOOL_TRUE: i64 = 1
54const TH_BOOL_FALSE: i64 = 0
59const TH_REQS_PER_GEN: i64 = TS_RV_BACKLOG
62const TH_DEADLINE_S: i64 = ACCEPT_TMO_S
63const TH_CHILD_ALARM_S: i64 = ACCEPT_TMO_S * 2
65const TH_O_ATTEMPTED: i64 = 0
66const TH_O_SERVED: i64 = 1
67const TH_O_GAP_RC: i64 = 2
68const TH_O_GAP_DONE: i64 = 3
69const TH_O_PORT: i64 = 4
70const TH_O_C1: i64 = 5
71const TH_O_C2: i64 = 6
72const TH_GAP_UNRUN: i64 = 0 - 999
74const TH_C_OK: i64 = 0
75const TH_C_ACQUIRE: i64 = 2
76const TH_C_BIND: i64 = 3
77const TH_C_READY: i64 = 4
78const TH_C_ACCEPT: i64 = 5
79const TH_C_BADREQ: i64 = 6
80const TH_C_WRITE: i64 = 7
81const TH_SOCK_ARMED: *u8 = "/tmp/nx_ts_handoff_gate/armed.sock" as *u8
82const TH_SOCK_NEG: *u8 = "/tmp/nx_ts_handoff_gate/neg.sock" as *u8
83const TH_DIR: *u8 = "/tmp/nx_ts_handoff_gate" as *u8

functions

85func g_putn(v: i64) -> i64
called by 2: gqth_report calls 2: sys_writesys_mmap
96func gq(label: *u8, got: i64, want: i64, ctr: *i64) -> i64
called by 1: main calls 3: gv_checkg_putsg_putn
108func th_sa(sa: *u8, port: i64) -> i64
122func th_put1(fd: i64, v: i64) -> i64
127func th_get1(fd: i64) -> i64
137func th_listen(port: i64, reuse: i64) -> i64
157func th_find_port(from: i64) -> i64
called by 1: main calls 2: th_listensys_close
169func th_exchange(fd: i64) -> i64
176func th_drive_one(port: i64) -> i64
190func th_generation(armed: i64, port: i64, sock_path: *u8, reqs: i64) -> i64
227func th_serve_rv(rv: i64, listen_fd: i64, armed: i64) -> i64
246func ts_swap_run(armed: i64, port: i64, sock_path: *u8, out: *i64) -> i64
331func th_report(tag: *u8, drops: i64, out: *i64) -> i64
called by 1: main calls 2: g_putsg_putn
344func main() -> i64