code wiki / _hdl_build / nx_registry_lock.nx

nx_registry_lock.nx source

↩ module page · 81 lines · 4085 B

1// nx_registry_lock.nx -- the SHARED race-free lock PRIMITIVE, extracted (DRY, rule 15) from 2// nx_registry_txn so every queue WRITER (reconcile / mark_migrate / express_lane + side-organs 3// + the pulse/autorun daemons) can acquire ONE serializable, crash-safe, drvfs-portable lock 4// around its read-modify-write of assignment_queue.tsv / row_markers.tsv. This is the hardware 5// rung the operator asked for ("build the coordination from the hardware rung up so we can keep 6// moving in parallel through daemons / orchestration"). 7// 8// PRIMITIVE: an O_CREAT|O_EXCL lockfile (the kernel guarantees exactly ONE creator wins -- no 9// TOCTOU) stamped with the holder's acquire-epoch; a contender spin-serializes and STEALS a lock 10// older than RT_STALE_SEC (a crashed holder, so the beat can never wedge). It deliberately does 11// NOT use flock(2): flock is dead on /mnt/c drvfs AND the rv64->x86_64 syscall map (32->73) only 12// activates on an nx_cc self-host redeploy (currently gated behind a RED cc_equiv run). O_EXCL 13// needs neither -- it works today on drvfs with NO compiler redeploy. 14// 15// S-CLASS EXCEED over ad_acquire_lock (hard-fails on contention, no staleness recovery) and over 16// atomic-rename-alone (saves a torn file but LOSES updates): this serializes contenders AND 17// self-heals a crashed holder = serializable + crash-safe + drvfs-portable. 18// 19// MAINLESS LIBRARY: a consumer imports THIS file only (it pulls in nx_syscalls transitively) and 20// must NOT also `import "nx_syscalls.nx"` directly -- the proven nx_assign_core<-nx_dep_audit 21// convention, which dodges the double-import rc=6. Gate-proven (no separate gate authored) by 22// nx_registry_txn's existing RTXN-GATE: fork 2 writers x N guarded increments -> lossless (==2N) 23// while the unguarded control loses (<2N). license_tier: ORIGINAL 24// 25// module: nishi-core.autonomy.registry_lock 26// depends: nishi-core.sys.syscalls 27// capability: SHARED_REGISTRY_WRITE_LOCK_OEXCL 28import "nx_syscalls.nx" 29const RT_MAGIC_8000000: i64 = 8000000 30const RT_MAGIC_8192: i64 = 8192 31 32const RT_OEXCL: i64 = 193 // O_CREAT(0x40)|O_EXCL(0x80)|O_WRONLY(0x1) 33const RT_MODE: i64 = 420 // 0644 34const RT_STALE_SEC: i64 = 30 // steal a lockfile whose stamped epoch is older than this (crashed holder) 35 36func rt_now() -> i64 { return sys_now_realtime_sec() } 37func rt_unlink(path: *u8) -> i64 { return __syscall(263, AT_FDCWD, path as i64, 0, 0, 0, 0) } // unlinkat(AT_FDCWD, path, 0) 38 39func rt_writeint_fd(fd: i64, v: i64) -> i64 { 40 let b: *u8 = sys_mmap(28) 41 var m: i64 = v 42 if m == 0 { b[0] = 48 as u8; sys_write(fd, b, 1); return 0 } 43 let t: *u8 = sys_mmap(28) 44 var k: i64 = 0 45 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 46 var i: i64 = 0 47 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 48 sys_write(fd, b, k) 49 return 0 50} 51func rt_readint(path: *u8) -> i64 { 52 let fd: i64 = sys_openat_rd(path) 53 if fd < 0 { return 0 } 54 let b: *u8 = sys_mmap(64) 55 let n: i64 = sys_read(fd, b, 63) 56 sys_close(fd) 57 var v: i64 = 0 58 var i: i64 = 0 59 while i < n { if b[i] >= (48 as u8) { if b[i] <= (57 as u8) { v = v * 10 + ((b[i] as i64) - 48) } } i = i + 1 } 60 return v 61} 62 63// ACQUIRE: spin-serialize on an O_CREAT|O_EXCL lockfile; steal a stale lock (age > RT_STALE_SEC). 64// returns the held fd (>=0) or -1 (gave up). S-class: serializes (not hard-fail) + self-heals a crash. 65func rt_lock(lockpath: *u8) -> i64 { 66 var tries: i64 = 0 67 while tries < RT_MAGIC_8000000 { 68 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, lockpath as i64, RT_OEXCL, RT_MODE, 0, 0) 69 if fd >= 0 { 70 rt_writeint_fd(fd, rt_now()) // stamp acquire-epoch for staleness detection by contenders 71 return fd 72 } 73 if (tries % RT_MAGIC_8192) == 0 { 74 let held: i64 = rt_readint(lockpath) 75 if held > 0 { if rt_now() - held > RT_STALE_SEC { rt_unlink(lockpath) } } 76 } 77 tries = tries + 1 78 } 79 return 0 - 1 80} 81func rt_unlock(lockpath: *u8, fd: i64) -> i64 { sys_close(fd); rt_unlink(lockpath); return 0 }