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}