code wiki / _hdl_build / nx_registry_lock.nx
nx_registry_lock.nx
buildroot/runtime/_hdl_build/nx_registry_lock.nx
about
nx_registry_lock.nx -- the SHARED race-free lock PRIMITIVE, extracted (DRY, rule 15) from
nx_registry_txn so every queue WRITER (reconcile / mark_migrate / express_lane + side-organs
+ the pulse/autorun daemons) can acquire ONE serializable, crash-safe, drvfs-portable lock
around its read-modify-write of assignment_queue.tsv / row_markers.tsv. This is the hardware
rung the operator asked for ("build the coordination from the hardware rung up so we can keep
moving in parallel through daemons / orchestration").
PRIMITIVE: an O_CREAT|O_EXCL lockfile (the kernel guarantees exactly ONE creator wins -- no
TOCTOU) stamped with the holder's acquire-epoch; a contender spin-serializes and STEALS a lock
older than RT_STALE_SEC (a crashed holder, so the beat can never wedge). It deliberately does
NOT use flock(2): flock is dead on /mnt/c drvfs AND the rv64->x86_64 syscall map (32->73) only
activates on an nx_cc self-host redeploy (currently gated behind a RED cc_equiv run). O_EXCL
needs neither -- it works today on drvfs with NO compiler redeploy.
S-CLASS EXCEED over ad_acquire_lock (hard-fails on contention, no staleness recovery) and over
atomic-rename-alone (saves a torn file but LOSES updates): this serializes contenders AND
self-heals a crashed holder = serializable + crash-safe + drvfs-portable.
MAINLESS LIBRARY: a consumer imports THIS file only (it pulls in nx_syscalls transitively) and
must NOT also `import "nx_syscalls.nx"` directly -- the proven nx_assign_core<-nx_dep_audit
convention, which dodges the double-import rc=6. Gate-proven (no separate gate authored) by
nx_registry_txn's existing RTXN-GATE: fork 2 writers x N guarded increments -> lossless (==2N)
while the unguarded control loses (<2N). license_tier: ORIGINAL
module: nishi-core.autonomy.registry_lock
depends: nishi-core.sys.syscalls
capability: SHARED_REGISTRY_WRITE_LOCK_OEXCL
dependencies 1 imports · 13 importers
diagram shows first 10 each side; +0 more imports, +3 more importers in the complete lists below.
imports: nx_syscalls.nx
imported by: _express_lane_gate_authored.nxnx_ale_gap_filer.nxnx_ecomat_beat.nxnx_ecomat_beat_gate.nxnx_ecomat_target_emit.nxnx_express_lane.nxnx_mark_migrate.nxnx_novel_close.nxnx_reader_coordinate.nxnx_reconcile.nxnx_registry_txn.nxnx_stackaudit_targets.nxnx_team_out_lock_gate.nx
structs
| none |
consts
| 29 | const RT_MAGIC_8000000: i64 = 8000000 |
| 30 | const RT_MAGIC_8192: i64 = 8192 |
| 32 | const RT_OEXCL: i64 = 193 // O_CREAT(0x40)|O_EXCL(0x80)|O_WRONLY(0x1) |
| 33 | const RT_MODE: i64 = 420 // 0644 |
| 34 | const RT_STALE_SEC: i64 = 30 // steal a lockfile whose stamped epoch is older than this (crashed holder) |
functions
| 36 | func rt_now() -> i64 { return sys_now_realtime_sec() } |
| 37 | func rt_unlink(path: *u8) -> i64 { return __syscall(263, AT_FDCWD, path as i64, 0, 0, 0, 0) } // unlinkat(AT_FDCWD, path, 0) |
| 39 | func rt_writeint_fd(fd: i64, v: i64) -> i64 |
| 51 | func rt_readint(path: *u8) -> i64 |
| 65 | func rt_lock(lockpath: *u8) -> i64 |
| 81 | func rt_unlock(lockpath: *u8, fd: i64) -> i64 { sys_close(fd); rt_unlink(lockpath); return 0 } |