code wiki / _hdl_build / nx_arbiter_gate.nx

nx_arbiter_gate.nx source

↩ module page · 94 lines · 6339 B

1// nx_arbiter_gate.nx -- proves the arbitration primitive (nx_arbiter): a kernel-enforced exclusive lock 2// serializes concurrent writers on a shared resource. Asserts: acquire succeeds; while held, a second 3// independent try is DENIED (mutual exclusion); after release the lock is free again; the lock file lives under 4// the stable shared LOCKDIR (not /tmp). Uses a PER-WORKSTREAM test resource (via NISHI_WSID) so concurrent 5// sibling gate runs in the 21-workstream fleet don't contend on the same test lock. GREEN iff all 5 pass. license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "nx_arbiter.nx" 8import "nx_runpath.nx" 9import "nx_gate_verdict.nx" // D001: inherit the verdict contract instead of re-emitting its strings 10import "nx_lease.nx" // ls_pid(): make the test resource unique per RUN, not just per workstream 11 12func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 13func g_starts(a: *u8, b: *u8) -> i64 { var i: i64=0; while b[i]!=(0 as u8){ if a[i]!=b[i]{return 0} i=i+1 } return 1 } 14func g_row(id: *u8, ok: i64, pass: *i64) -> i64 { g_w(" "); g_w(id); g_w(": "); if ok==1 { g_w("OK\n"); pass[0]=pass[0]+1 } else { g_w("FAIL\n") } return 0 } 15 16func main() -> i64 { 17 let pass: *i64 = sys_mmap(8) as *i64; pass[0] = 0 18 g_w("=== NX-ARBITER GATE (kernel-enforced arbitration on shared sinks) ===\n") 19 20 // per-workstream test resource: "nxarb_test_<wsid>" 21 let wsid: *u8 = sys_mmap(64); rp_wsid(wsid) 22 let res: *u8 = sys_mmap(128); var o: i64 = 0; var i: i64 = 0 23 let pfx: *u8 = "nxarb_test_"; while pfx[i]!=(0 as u8){ res[o]=pfx[i]; o=o+1; i=i+1 } 24 i=0; while wsid[i]!=(0 as u8){ res[o]=wsid[i]; o=o+1; i=i+1 } 25 // ---- PER-PROCESS, NOT JUST PER-WORKSTREAM (2026-08-01) ---- 26 // The wsid suffix above was added so sibling gate runs across the 21-workstream fleet would not 27 // contend on one test lock. It does not cover TWO RUNS IN THE SAME WORKSTREAM, and that case is not 28 // hypothetical: the mutation probe runs this gate repeatedly, and a hand run alongside it scored 4/6 29 // -- the two rows that must ACQUIRE the lock failed while exclusion and bounded-wait still passed. 30 // Those failures were a RACE ARTIFACT, not a defect in nx_arbiter, and they are indistinguishable 31 // from a real regression in the log. A gate whose result depends on who else is running is not a 32 // measurement. 33 // LAW: A GATE THAT TESTS EXCLUSION MUST OWN A RESOURCE NOBODY ELSE CAN CLAIM -- otherwise it tests 34 // the scheduler, not the primitive. Appending the pid makes the resource unique per RUN, which is 35 // what hermetic means here. Cross-run contention was never this gate's subject. 36 res[o]=45 as u8; o=o+1 // '-' 37 var pidv: i64 = ls_pid() 38 let pdt: *u8 = sys_mmap(24); var pk: i64 = 0 39 if pidv==0 { pdt[0]=48 as u8; pk=1 } 40 while pidv>0 { pdt[pk]=(48+(pidv%10)) as u8; pidv=pidv/10; pk=pk+1 } 41 var pz: i64 = 0 42 while pz<pk { res[o]=pdt[pk-1-pz]; o=o+1; pz=pz+1 } 43 res[o]=0 as u8 44 45 let fd1: i64 = fl_acquire(res, 10, 50) 46 g_row("acquire the lock (bounded wait)" as *u8, (fd1 >= 0) as i64, pass) 47 48 // while held, an independent try MUST be denied -> mutual exclusion 49 let fd2: i64 = fl_try(res) 50 g_row("MUTUAL EXCLUSION: second try denied while held" as *u8, (fd2 == (0 - 1)) as i64, pass) 51 52 // ---- MUTATION-DERIVED TOOTH: THE BOUNDED-WAIT RETRY PATH (2026-08-01, debt 1785604588) ---- 53 // nx_gate_mutation_probe scored this pair 3/4 with a survivor at nx_arbiter.nx:54 -- the ` + ` in 54 // `i = i + 1`, the RETRY COUNTER of fl_acquire's bounded-wait loop. It survived because every call 55 // above acquires an UNCONTENDED lock: fl_try succeeds on the first iteration and returns, so the 56 // loop body never executes and the counter is never used. The timeout path -- the entire reason 57 // fl_acquire exists rather than fl_try -- was untested. 58 // Running it against a HELD lock is the only way to reach that code: fl_try must fail `tries` times 59 // and the loop must then EXIT. With the counter mutated the loop cannot terminate (i decrements, so 60 // `i < tries` stays true forever) and the gate hangs instead of returning -- which the probe's run 61 // deadline converts into a non-zero rc. Either way the mutant dies; only correct code returns -1. 62 // LAW: A BOUNDED-WAIT LOOP IS ONLY TESTED UNDER CONTENTION. Acquiring a free lock exercises the 63 // fast path and nothing else, so a retry/backoff/timeout bug ships invisibly. 64 // tries=3 nap=10ms keeps this under ~30ms; the deliberate cost is that the gate now blocks briefly. 65 let fd_to: i64 = fl_acquire(res, 3, 10) 66 g_row("BOUNDED WAIT: acquire against a HELD lock exhausts its retries and TIMES OUT" as *u8, (fd_to == (0 - 1)) as i64, pass) 67 if fd_to >= 0 { fl_release(fd_to) } 68 69 fl_release(fd1) 70 71 // after release, the lock is free again 72 let fd3: i64 = fl_try(res) 73 g_row("lock is free again after release" as *u8, (fd3 >= 0) as i64, pass) 74 fl_release(fd3) 75 76 // lock file is under the stable shared LOCKDIR, not /tmp 77 let lp: *u8 = sys_mmap(512); fl_path(res, lp) 78 g_row("lock under /home/.../.nishi/locks (stable, shared)" as *u8, g_starts(lp, "/home/elderwesto/.nishi/locks/" as *u8), pass) 79 g_row("LIAR-KILL: lock is NOT under /tmp" as *u8, (g_starts(lp, "/tmp" as *u8) == 0) as i64, pass) 80 81 // rows 5 -> 6 in LOCKSTEP with the tooth added above. Adding a row without moving this total is the 82 // half-landed-pair defect: every row would pass and the gate would still report RED. 83 // D001 MIGRATE-ON-TOUCH, BY HAND. nx_gate_dry_apply SKIPPED this gate: its counter is 84 // POINTER-INDEXED (`pass[0] == 6`, from an mmap'd slot) and shape F's operand scanner reads bare 85 // identifiers, so `pass[0]` does not match. Widening that scanner to accept bracket subscripts for 86 // ONE file would trade a known-safe bound for an unknown one across ~200 other gates -- the same 87 // call made for nx_singleness_gate. The bound stays; the author migrates his own gate. 88 let ctr: *i64 = gv_ctr() 89 ctr[0] = pass[0] 90 ctr[1] = 6 91 let rc: i64 = gv_verdict("ARBITER-GATE" as *u8, ctr, "kernel-enforced arbitration: exclusion, bounded-wait timeout, release, stable lockdir" as *u8) 92 sys_exit(rc) 93 return rc 94}