code wiki / (root) / nx_poolgov.nx

nx_poolgov.nx source

↩ module page · 185 lines · 8531 B

1// nx_poolgov.nx -- THE POOL-WIDTH GOVERNOR, in the PRIMITIVE layer where production organs can reach it. 2// 3// WHY IT LIVES HERE (seq1450): I first built this in _hdl_build/ and wiring it into nx_torrent_get failed 4// with `nx_compile_x86: expand_imports failed`. The layering edge is DIRECTED and was undocumented: 5// _hdl_build/ organs MAY import runtime/ libs, but a runtime/ organ may NOT import _hdl_build/. 6// runtime/ = PRIMITIVE layer, _hdl_build/ = ORGAN/TOOL layer, and primitives may not depend on tools. 7// LAW: BUILD THE PRIMITIVE IN THE LAYER ITS CONSUMERS CAN REACH -- the import graph, not taste, decides. 8// 9// WHAT IT FIXES (seq1402/seq1410): nx_torrent_get.nx carried `let MAXP: i64 = 32` -- a hardcoded literal 10// (rule-11) that made every machine identical: NAS (4c/8t), west server, laptop, any future box all ran 11// 32 slots, sized for hardware nobody measured. And 32 was PER-INSTANCE: nx_procchurn measured 143 12// forks/sec = ~9 concurrent instances = ~288 concurrent slots on an 8-thread box. NOBODY CHOSE 143. 13// LAW: A PER-INSTANCE LIMIT IS NOT A LIMIT -- N instances multiply it and nobody ever picks N. 14// 15// WHAT IT DELIBERATELY DOES NOT DO: throttle the connection CYCLING. Constant cycling IS the BitTorrent 16// throughput strategy (continuous optimistic-unchokes vs fast-burst-then-freeze). Slowing it would attack 17// the feature to flatten a graph -- operator rule 25. Only WIDTH is governed; inside the budget the pool 18// stays exactly as aggressive as before. 19// 20// The load parse is duplicated from nx_ctxtop_lib BY LAYER NECESSITY, not by neglect -- the organ-layer 21// copy cannot be imported down here. Correct resolution: the organ layer should import THIS one (the 22// allowed direction) and its copy retire. Tracked, not pretended away. 23// license_tier: ORIGINAL Read-only. No hw writes (Rule 26). 24import "nx_syscalls.nx" 25import "nx_hw.nx" 26// dirent64 accessors inlined DELIBERATELY: nx_proc_ctl.nx lives ONLY in _hdl_build/ (the runtime/ copy is 27// .dupe-reconciled), and a primitive may not import the organ layer -- the same seq1450 rule, hit 28// TRANSITIVELY through a dependency rather than directly. Layout: d_ino(8) d_off(8) d_reclen(u16@16) 29// d_type(u8@18) d_name(@19). 30func pg_dreclen(rec: *u8) -> i64 { return (rec[16] as i64) | ((rec[17] as i64) << 8) } 31func pg_dname(rec: *u8) -> *u8 { return ((rec as i64) + 19) as *u8 } 32 33const PG_UNKNOWN_LOAD: i64 = 0 - 1 34const PG_DIRBUF: i64 = 65536 35const PG_PATHBUF: i64 = 256 36const PG_CMDBUF: i64 = 8192 37const PG_LOADBUF: i64 = 128 38const PG_SCALE: i64 = 100 39const PG_DOT: i64 = 46 40 41// ---- the machine we are ACTUALLY on ---- 42// sched_getaffinity (not /proc/cpuinfo): returns the CPUs THIS PROCESS may run on, so it is correct under 43// cgroups / taskset / containers where cpuinfo reports host cores the process can never touch. 44func pg_budget_from_hw(slots_per_cpu: i64, floor: i64) -> i64 { 45 if floor < 1 { return 0 - 1 } 46 if slots_per_cpu < 1 { return floor } 47 let n: i64 = nx_hw_cpu_count() 48 if n < 1 { return floor } 49 let b: i64 = n * slots_per_cpu 50 if b < floor { return floor } 51 return b 52} 53 54func pg_load_centi() -> i64 { 55 let b: *u8 = sys_mmap(PG_LOADBUF) 56 let fd: i64 = sys_openat_rd("/proc/loadavg" as *u8) 57 if fd < 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD } 58 let n: i64 = sys_read(fd, b, PG_LOADBUF - 1) 59 sys_close(fd) 60 if n <= 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD } 61 var whole: i64 = 0 62 var seen: i64 = 0 63 var i: i64 = 0 64 while i < n { 65 let c: i64 = b[i] as i64 66 var d: i64 = 0 67 if c >= 48 { if c <= 57 { d = 1 } } 68 if d == 1 { whole = whole * 10 + (c - 48); seen = 1; i = i + 1 } else { i = n } 69 } 70 if seen == 0 { sys_munmap(b, PG_LOADBUF); return PG_UNKNOWN_LOAD } 71 var frac: i64 = 0 72 var got: i64 = 0 73 var q: i64 = 0 74 while q < n { 75 if b[q] == (PG_DOT as u8) { 76 var k: i64 = q + 1 77 while got < 2 { 78 if k >= n { got = 2 } else { 79 let e: i64 = b[k] as i64 80 var dd: i64 = 0 81 if e >= 48 { if e <= 57 { dd = 1 } } 82 if dd == 1 { frac = frac * 10 + (e - 48); got = got + 1 } else { got = 2 } 83 k = k + 1 84 } 85 } 86 q = n 87 } else { q = q + 1 } 88 } 89 sys_munmap(b, PG_LOADBUF) 90 return whole * PG_SCALE + frac 91} 92 93// How many processes share this budget RIGHT NOW. This is what makes the limit a limit. 94// ONE /proc walk, paid ONCE at pool construction -- never per request, never inside a loop. 95// Returns >=1 (this process counts), so a caller can always divide by it. 96func pg_count_procs(needle: *u8) -> i64 { 97 var nn: i64 = 0 98 while needle[nn] != (0 as u8) { nn = nn + 1 } 99 if nn == 0 { return 1 } 100 let dbuf: *u8 = sys_mmap(PG_DIRBUF) 101 let path: *u8 = sys_mmap(PG_PATHBUF) 102 let cbuf: *u8 = sys_mmap(PG_CMDBUF) 103 var cnt: i64 = 0 104 let fd: i64 = sys_openat_rd("/proc" as *u8) 105 if fd >= 0 { 106 var run: i64 = 1 107 while run == 1 { 108 let dn: i64 = sys_getdents64(fd, dbuf, PG_DIRBUF) 109 if dn <= 0 { run = 0 } else { 110 var off: i64 = 0 111 while off < dn { 112 let rec: *u8 = ((dbuf as i64) + off) as *u8 113 let reclen: i64 = pg_dreclen(rec) 114 if reclen <= 0 { off = dn } else { 115 let nm: *u8 = pg_dname(rec) 116 if nm[0] >= (48 as u8) { if nm[0] <= (57 as u8) { 117 var p: i64 = 0 118 let pre: *u8 = "/proc/" as *u8 119 var a: i64 = 0 120 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 } 121 a = 0 122 while nm[a] != (0 as u8) { path[p] = nm[a]; p = p + 1; a = a + 1 } 123 let suf: *u8 = "/cmdline" as *u8 124 a = 0 125 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 } 126 path[p] = 0 as u8 127 let cf: i64 = sys_openat_rd(path) 128 if cf >= 0 { 129 let cn: i64 = sys_read(cf, cbuf, PG_CMDBUF - 1) 130 sys_close(cf) 131 if cn > 0 { 132 var x: i64 = 0 133 var found: i64 = 0 134 while x + nn <= cn { 135 if found == 0 { 136 var j: i64 = 0 137 var m: i64 = 1 138 while j < nn { if cbuf[x + j] != needle[j] { m = 0; j = nn } else { j = j + 1 } } 139 if m == 1 { found = 1 } 140 } 141 x = x + 1 142 } 143 if found == 1 { cnt = cnt + 1 } 144 } 145 } 146 } } 147 off = off + reclen 148 } 149 } 150 } 151 } 152 sys_close(fd) 153 } 154 sys_munmap(dbuf, PG_DIRBUF) 155 sys_munmap(path, PG_PATHBUF) 156 sys_munmap(cbuf, PG_CMDBUF) 157 if cnt < 1 { return 1 } 158 return cnt 159} 160 161// Squeeze is PROPORTIONAL, not a cliff: above the ceiling width scales by max/load, so 2x the ceiling 162// halves the slots. A step function would oscillate pools between saturation and idle. 163// FAIL-SAFE: an unreadable load yields wmin -- not knowing the pressure is not permission to grab slots. 164func pg_width(total_budget: i64, n_instances: i64, load_centi: i64, 165 max_centi: i64, wmin: i64, wmax: i64) -> i64 { 166 if wmin < 1 { return 0 - 1 } 167 if wmax < wmin { return 0 - 1 } 168 if total_budget < 1 { return wmin } 169 if max_centi < 1 { return wmin } 170 if load_centi < 0 { return wmin } 171 var n: i64 = n_instances 172 if n < 1 { n = 1 } 173 var w: i64 = total_budget / n 174 if load_centi > max_centi { w = (w * max_centi) / load_centi } 175 if w < wmin { w = wmin } 176 if w > wmax { w = wmax } 177 return w 178} 179 180func pg_aggregate(width: i64, n_instances: i64) -> i64 { 181 if width < 0 { return 0 - 1 } 182 var n: i64 = n_instances 183 if n < 1 { n = 1 } 184 return width * n 185}