code wiki / _hdl_build / nx_registry_txn.nx
nx_registry_txn.nx
buildroot/runtime/_hdl_build/nx_registry_txn.nx
about
nx_registry_txn.nx -- the RACE-FREE registry-write COORDINATOR (operator 2026-06-14: "fix this race
callout from the hardware rung up to be S-class exceed").
THE RACE: nx_reconcile / nx_mark_migrate / nx_express_lane all do read-modify-write on
assignment_queue.tsv / row_markers.tsv with NO lock. Atomic-rename saves them from a TORN file
(corruption) but NOT from a LOST UPDATE: if two run concurrently both read the same base, both
rewrite, the second clobbers the first. (Confirmed: el_apply_mark renames, reconcile/mark_migrate
truncate-write; none lock.)
HARDWARE RUNG: the lock primitive flock was silently broken -- rv64 syscall 32 had NO entry in the
rv64->x86_64 map (nx_x86_64_ctx.nx) so it fell through to x86_64 32 = dup2 (a no-op "lock"). FIXED
(32->73). And flock is anyway dead on /mnt/c drvfs. So the drvfs-safe mutual-exclusion primitive is
an O_CREAT|O_EXCL lockfile (kernel guarantees ONE creator wins, no TOCTOU) + atomic renameat.
S-CLASS EXCEED over the incumbent ad_acquire_lock (which HARD-FAILS on contention with NO staleness
recovery): this coordinator SPIN-SERIALIZES contenders AND self-heals a stale lock left by a crashed
holder (the lockfile stamps its acquire-epoch; a lock older than RT_STALE_SEC is stolen) -- so it is
serializable + crash-safe + drvfs-portable, exceeding flock (drvfs-dead), atomic-rename-alone (loses
updates), and ad_acquire_lock (deadlocks on a crashed holder).
SELF-GATE (no-args): fork 2 writers, each does N lock-guarded increments of a counter file. GUARDED ->
final == 2N (no lost update). UNGUARDED control -> final < 2N (the race is real, the lock necessary).
GREEN iff guarded==2N AND unguarded<2N. license_tier: ORIGINAL
LOCK PRIMITIVE EXTRACTED 2026-06-14: rt_lock/rt_unlock (+ rt_now/rt_unlink/rt_readint/
rt_writeint_fd and the RT_OEXCL/RT_MODE/RT_STALE_SEC consts) now live in the shared mainless
lib nx_registry_lock.nx so the queue writers + daemons can share ONE serializable lock (DRY,
rule 15). This organ now imports the lib ONLY (NOT nx_syscalls directly -- the lib pulls it in
transitively; the nx_assign_core<-nx_dep_audit convention that dodges the double-import rc=6).
This file stays the GATE that proves the lib (RTXN-GATE = fork-2-writers losslessness).
dependencies 1 imports · 0 importers
imports: nx_registry_lock.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 33 | const RT_COUNTER: *u8 = "knowledge/status/_rtxn_counter" |
| 34 | const RT_LOCK: *u8 = "knowledge/status/_rtxn_counter.lock" |
| 35 | const RT_TMP0: *u8 = "knowledge/status/_rtxn.tmp0" |
| 36 | const RT_TMP1: *u8 = "knowledge/status/_rtxn.tmp1" |
| 37 | const RT_ITERS: i64 = 40 |
functions
| 39 | func rt_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 } called by 1: rt_gate |
| 40 | func rt_wn(fd: i64, v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64=v; if m<0 {m=0-m; sys_write(fd,"-" as *u8,1)}; let t: *u8 = sys_mmap(28); var k: i64=0; if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}; var i: i64=0; while i<k {bb[i]=t[k-1-i]; i=i+1}; sys_write(fd, bb, k); return 0 } called by 1: rt_gate |
| 43 | func rt_setint(path: *u8, v: i64) -> i64 |
| 54 | func rt_inc(counter: *u8, lockpath: *u8, tmp: *u8, guarded: i64) -> i64 |
| 64 | func rt_loop(counter: *u8, lockpath: *u8, tmp: *u8, guarded: i64, iters: i64) -> i64 |
| 71 | func rt_run(guarded: i64, iters: i64) -> i64 |
| 84 | func rt_gate(fd: i64, pos: i64, neg: i64, expect: i64, ok: i64, epoch: i64) -> i64 |
| 94 | func main() -> i64 |