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}