code wiki / _hdl_build / nx_arbiter.nx

nx_arbiter.nx source

↩ module page · 63 lines · 3290 B

1// nx_arbiter.nx -- ARBITRATION primitive for conflict-free PARALLEL workstreams (pillar 3 of the orchestration 2// fix). Some sinks CANNOT be isolated -- they are inherently shared: the NAS push target (one wiki doc-root), 3// _offc/ installs, the memory files (a sibling clobbered MEMORY.md + genesis_lineage.tsv mid-write today), the 4// WMS queue, the git repo. For those, serialize concurrent writers with a KERNEL-ENFORCED advisory lock 5// (flock) keyed by a named resource. A workstream takes the lock, does its critical write, releases; siblings 6// contending on the SAME resource wait their turn. 7// 8// Uses sys_flock (nx_syscalls.nx, the RV64-32 -> x86_64-73 wrapper) -- NOT raw __syscall(73): the compiler 9// translates rv64 numbers, so the x86_64 number passed raw lands on the WRONG syscall (proven: raw 73 -> EFAULT). 10// Imports ONLY nx_syscalls.nx (NOT the legacy nx_flock.nx organ) to avoid the double-import rc=6 trap. 11// fl_try(resource) -> fd>=0 if acquired now, -1 if busy/err (non-blocking). 12// fl_acquire(resource, tries, ms) -> fd>=0 (bounded wait), -1 on timeout. 13// fl_release(fd) -> unlock + close. 14// Lock files: /home/.../.nishi/locks/<resource>.lock (shared BY DESIGN = the rendezvous point). license_tier: ORIGINAL 15import "nx_syscalls.nx" 16const FL_MAGIC_1000000: i64 = 1000000 17 18const FL_LOCKDIR: *u8 = "/home/elderwesto/.nishi/locks" 19const FL_EXNB: i64 = 6 // SYS_LOCK_EX(2) | SYS_LOCK_NB(4) 20const FL_UN: i64 = 8 // SYS_LOCK_UN 21 22func fl_mkdir() -> i64 { let ld: *u8 = FL_LOCKDIR; __syscall(83, ld as i64, 448, 0, 0, 0, 0); return 0 } 23func fl_nap(ms: i64) -> i64 { let ts: *i64 = sys_mmap(16) as *i64; ts[0] = 0; ts[1] = ms * FL_MAGIC_1000000; __syscall(35, ts as i64, 0, 0, 0, 0, 0); return 0 } 24 25// build "<LOCKDIR>/<resource>.lock" into out (LOCKDIR bound to a local before indexing). returns length. 26func fl_path(resource: *u8, out: *u8) -> i64 { 27 let ld: *u8 = FL_LOCKDIR 28 var o: i64 = 0; var i: i64 = 0 29 while ld[i]!=(0 as u8){ out[o]=ld[i]; o=o+1; i=i+1 } 30 out[o]=47 as u8; o=o+1 31 i=0; while resource[i]!=(0 as u8){ out[o]=resource[i]; o=o+1; i=i+1 } 32 let ext: *u8 = ".lock"; i=0; while ext[i]!=(0 as u8){ out[o]=ext[i]; o=o+1; i=i+1 } 33 out[o]=0 as u8 34 return o 35} 36 37// non-blocking exclusive try-lock on a resource. returns the held fd (>=0) or -1 (busy/err). 38func fl_try(resource: *u8) -> i64 { 39 fl_mkdir() 40 let lp: *u8 = sys_mmap(512); fl_path(resource, lp) 41 let fd: i64 = sys_openat_wr(lp, 0x180) // O_WRONLY|O_CREAT, 0600 (content irrelevant; the fd carries the lock) 42 if fd < 0 { return 0 - 1 } 43 if sys_flock(fd, FL_EXNB) != 0 { sys_close(fd); return 0 - 1 } // LOCK_EX|LOCK_NB; 0 ok, -EWOULDBLOCK if held 44 return fd 45} 46 47// bounded-wait acquire: try up to `tries` times, napping `nap_ms` between. returns the held fd or -1 (timeout). 48func fl_acquire(resource: *u8, tries: i64, nap_ms: i64) -> i64 { 49 var i: i64 = 0 50 while i < tries { 51 let fd: i64 = fl_try(resource) 52 if fd >= 0 { return fd } 53 fl_nap(nap_ms) 54 i = i + 1 55 } 56 return 0 - 1 57} 58 59// release: LOCK_UN + close. siblings waiting on the resource can now proceed. 60func fl_release(fd: i64) -> i64 { 61 if fd >= 0 { sys_flock(fd, FL_UN); sys_close(fd) } 62 return 0 63}