code wiki / (root) / nx_ts_slot_router.nx

nx_ts_slot_router.nx source

↩ module page · 74 lines · 3251 B

1// nx_ts_slot_router.nx -- TS3: THE FRONT DOOR ITSELF (the thin daemon around nx_ts_slot_lib). 2// 3// Split from the decision core deliberately: the core is a LIBRARY so a gate can drive ts_slot_flip 4// directly and in-process, while this file is the runnable front door. One capability, one home -- 5// a gate that had to fork a binary just to exercise a pure decision would be testing the plumbing. 6// See nx_ts_slot_lib.nx for the full rung rationale, the standalone-by-design fence, and the scope 7// of the warm-up probe. 8// license_tier: ORIGINAL No hw writes (Rule 26). 9import "nx_ts_slot_lib.nx" 10 11// Exit code for a usage error, named rather than returned as a bare integer. 12const TSR_RC_USAGE: i64 = 2 13func main(argc: i64, argv: **u8) -> i64 { 14 if argc < TSR_ARGC_MIN { 15 tsr_puts("usage: nx_ts_slot_router <front_port> <slotA_port> <slotB_port> <state_path> [handoff_sock]\n" as *u8) 16 return TSR_RC_USAGE 17 } 18 let front: i64 = tsr_atoi(argv[TSR_A_FRONT] as *u8) 19 let ports: *i64 = (sys_mmap(TSR_OUT_BYTES)) as *i64 20 ports[TSR_SLOT_A] = tsr_atoi(argv[TSR_A_SLOTA] as *u8) 21 ports[TSR_SLOT_B] = tsr_atoi(argv[TSR_A_SLOTB] as *u8) 22 let state: *u8 = argv[TSR_A_STATE] as *u8 23 var sockp: *u8 = "" as *u8 24 if argc > TSR_A_SOCK { sockp = argv[TSR_A_SOCK] as *u8 } 25 26 // TS2 FIRST: arm the drain before a listener exists to be interrupted. 27 let dv: *i64 = (sys_mmap(TSR_OUT_BYTES)) as *i64 28 dv[0] = 0 29 let dfd: i64 = ts_drain_on_term(dv) 30 31 // ENVOY ORDERING: the forwarding buffer is allocated BEFORE the socket is acquired. 32 let buf: *u8 = sys_mmap(TSR_BUF_CAP) 33 34 // TS1: the front door takes its own listener from the handoff, so the one component that must 35 // never restart is itself replaceable without a gap. 36 let addr: *u8 = sys_mmap(TSR_SA_BYTES) 37 tsr_sa(addr, front) 38 let lv: *i64 = (sys_mmap(TSR_OUT_BYTES)) as *i64 39 lv[0] = 0 40 let lfd: i64 = ts_handoff_nodrop(sockp, addr, TSR_BACKLOG, lv) 41 if lfd < 0 { 42 tsr_puts("NX-TS-SLOT-ROUTER listener FAILED verdict=" as *u8); tsr_putn(lv[0]) 43 tsr_puts(" -- fail loud\n" as *u8) 44 return 1 45 } 46 tsr_puts("NX-TS-SLOT-ROUTER front=" as *u8); tsr_putn(front) 47 tsr_puts(" slotA=" as *u8); tsr_putn(ports[TSR_SLOT_A]) 48 tsr_puts(" slotB=" as *u8); tsr_putn(ports[TSR_SLOT_B]) 49 tsr_puts(" active=" as *u8); tsr_putn(ts_slot_active(state)) 50 tsr_puts(" drain_fd=" as *u8); tsr_putn(dfd) 51 tsr_puts("\n" as *u8) 52 53 var go: i64 = 1 54 var served: i64 = 0 55 while go == 1 { 56 let w: i64 = ts_drain_wait(lfd, dfd, TSD_WAIT_BLOCK) 57 if w == TSD_W_DRAIN { 58 go = 0 59 } else { 60 if w == TSD_W_CONN { 61 let cfd: i64 = sys_accept(lfd) 62 if cfd >= 0 { 63 sys_set_socket_timeout(cfd, ACCEPT_TMO_S) 64 // RE-READ THE CHOICE PER CONNECTION: no cached routing state can disagree with the file. 65 let slot: i64 = ts_slot_active(state) 66 if tsr_forward(cfd, ports[slot], buf) == 1 { served = served + 1 } 67 sys_close(cfd) 68 } 69 } } 70 } 71 tsr_puts("NX-TS-SLOT-ROUTER drained on TERM served=" as *u8); tsr_putn(served) 72 tsr_puts("\n" as *u8) 73 return 0 74}