code wiki / _hdl_build / nx_poolwidth_lib.nx

nx_poolwidth_lib.nx source

↩ module page · 184 lines · 9453 B

1// nx_poolwidth_lib.nx -- ONE headroom-derived width governor for every pool in the ecosystem (seq1402). 2// 3// THE DEFECT, MEASURED 2026-07-30: nx_torrent_get.nx:717 carries `let MAXP: i64 = 32` -- a hardcoded 4// literal (rule-11) -- and refills every free slot every 2s. That is 16 forks/sec PER INSTANCE. The box 5// measured **143 forks/sec at load 11.72 on 8 threads**, which is ~9 concurrent instances. NOBODY CHOSE 6// 143. It is 32 x N, and N is whatever the operator queued. 7// ★★LAW: A PER-INSTANCE LIMIT IS NOT A LIMIT. N instances multiply it and nobody ever picks N. 8// 9// ★WHAT THIS IS *NOT*: a backoff. nx_torrent_get.nx:711-716 documents constant connection cycling as the 10// BitTorrent throughput strategy (continuous optimistic-unchokes vs fast-burst-then-freeze). Throttling 11// the CYCLING would attack the feature to flatten a graph -- operator rule 25, build intelligence, never 12// strip it. This governs only the WIDTH; inside its budget the pool stays as aggressive as it ever was. 13// 14// The same law already bit the crawl pool (seq1288 saturation-wedge: "pool width must be HEADROOM-DERIVED"). 15// Two lanes, one defect => ONE primitive, not two fixes (rule 15). 16// license_tier: ORIGINAL Pure. No hw writes (Rule 26). 17import "nx_syscalls.nx" 18import "nx_hw.nx" 19import "nx_ctxtop_lib.nx" 20import "nx_proc_ctl.nx" 21 22const PW_DIRBUF: i64 = 65536 23const PW_PATHBUF: i64 = 256 24const PW_CMDBUF: i64 = 8192 25const PW_LOADBUF: i64 = 128 26 27// Live /proc/loadavg in centi-load, or -1. Reuses ct_load_centi (rule 15: the parse is already proven 28// by nx_ctxtop_gate T9/T10/T11 against the REAL measured line -- a second copy would be a second bug). 29func pw_load_centi() -> i64 { 30 let b: *u8 = sys_mmap(PW_LOADBUF) 31 let fd: i64 = sys_openat_rd("/proc/loadavg" as *u8) 32 if fd < 0 { sys_munmap(b, PW_LOADBUF); return 0 - 1 } 33 let n: i64 = sys_read(fd, b, PW_LOADBUF - 1) 34 sys_close(fd) 35 if n <= 0 { sys_munmap(b, PW_LOADBUF); return 0 - 1 } 36 let v: i64 = ct_load_centi(b, n) 37 sys_munmap(b, PW_LOADBUF) 38 return v 39} 40 41// How many processes share this budget right now? Counts /proc cmdlines containing `needle`. 42// THIS IS WHAT MAKES THE LIMIT A LIMIT: a per-instance cap cannot bound aggregate load by construction 43// (seq1402 -- 32 x 9 instances = 288 slots against a 32 budget, and nobody ever picks N). 44// Cost is ONE /proc walk, paid ONCE at pool construction -- never per request, never in a loop. 45// Returns at least 1 (this process itself counts), so a caller can always divide by it safely. 46func pw_count_procs(needle: *u8) -> i64 { 47 var nn: i64 = 0 48 while needle[nn] != (0 as u8) { nn = nn + 1 } 49 if nn == 0 { return 1 } 50 let dbuf: *u8 = sys_mmap(PW_DIRBUF) 51 let path: *u8 = sys_mmap(PW_PATHBUF) 52 let cbuf: *u8 = sys_mmap(PW_CMDBUF) 53 var cnt: i64 = 0 54 let fd: i64 = sys_openat_rd("/proc" as *u8) 55 if fd >= 0 { 56 var run: i64 = 1 57 while run == 1 { 58 let dn: i64 = sys_getdents64(fd, dbuf, PW_DIRBUF) 59 if dn <= 0 { run = 0 } else { 60 var off: i64 = 0 61 while off < dn { 62 let rec: *u8 = ((dbuf as i64) + off) as *u8 63 let reclen: i64 = dirent_reclen(rec) 64 if reclen <= 0 { off = dn } else { 65 let nm: *u8 = dirent_name(rec) 66 if nm[0] >= (48 as u8) { if nm[0] <= (57 as u8) { 67 var p: i64 = 0 68 let pre: *u8 = "/proc/" as *u8 69 var a: i64 = 0 70 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 } 71 a = 0 72 while nm[a] != (0 as u8) { path[p] = nm[a]; p = p + 1; a = a + 1 } 73 let suf: *u8 = "/cmdline" as *u8 74 a = 0 75 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 } 76 path[p] = 0 as u8 77 let cf: i64 = sys_openat_rd(path) 78 if cf >= 0 { 79 let cn: i64 = sys_read(cf, cbuf, PW_CMDBUF - 1) 80 sys_close(cf) 81 if cn > 0 { 82 var i: i64 = 0 83 var found: i64 = 0 84 while i + nn <= cn { 85 if found == 0 { 86 var j: i64 = 0 87 var m: i64 = 1 88 while j < nn { if cbuf[i + j] != needle[j] { m = 0; j = nn } else { j = j + 1 } } 89 if m == 1 { found = 1 } 90 } 91 i = i + 1 92 } 93 if found == 1 { cnt = cnt + 1 } 94 } 95 } 96 } } 97 off = off + reclen 98 } 99 } 100 } 101 } 102 sys_close(fd) 103 } 104 sys_munmap(dbuf, PW_DIRBUF) 105 sys_munmap(path, PW_PATHBUF) 106 sys_munmap(cbuf, PW_CMDBUF) 107 if cnt < 1 { return 1 } 108 return cnt 109} 110 111const PW_UNKNOWN_LOAD: i64 = 0 - 1 112 113// ---- FIRST ADOPTER of nx_hw_cpu_count (seq1410) ---- 114// CENSUS 2026-07-30: nx_hw_cpu_count had FIVE matches in 19,823 files -- 1 definition, 3 self-references, 115// 1 test comment, and ZERO external callers. The hardware-adaptivity primitive was 100pct DARK while 116// nx_torrent_get hardcoded MAXP=32. This function exists so the budget is MEASURED FROM THE MACHINE WE 117// ARE ACTUALLY ON, which is the operator's standing law (feedback-dynamic-hw-sizing-no-hardcoded-thread-counts): 118// Nishi on the NAS, the west server, this laptop or any future box must each use THAT box's capability. 119// 120// ★WHY sched_getaffinity AND NOT /proc/cpuinfo: it returns the CPUs THIS PROCESS MAY RUN ON, so it is 121// correct under cgroups / taskset / containers, where /proc/cpuinfo reports host cores the process can 122// never touch. Counting cpuinfo would size a 64-thread pool inside a --cpus=2 container. 123// 124// slots_per_cpu is POLICY (config-driven, rule 11); the CPU COUNT is MEASURED, never assumed. 125// Note slots_per_cpu=4 reproduces today's MAXP=32 on this 8-thread NAS -- behaviour-preserving HERE 126// while scaling correctly everywhere else, which is what makes this migration safe to land. 127func pw_budget_from_hw(slots_per_cpu: i64, floor: i64) -> i64 { 128 if floor < 1 { return 0 - 1 } 129 if slots_per_cpu < 1 { return floor } 130 let n: i64 = nx_hw_cpu_count() 131 if n < 1 { return floor } 132 let b: i64 = n * slots_per_cpu 133 if b < floor { return floor } 134 return b 135} 136 137// Derive THIS instance's pool width from an ecosystem-wide budget. 138// total_budget : concurrent slots the WHOLE ecosystem may hold (config; headroom-derived upstream) 139// n_instances : how many peers share it right now (>=1; 0/negative treated as 1, never as divide-by-zero) 140// load_centi : live /proc/loadavg x100, or -1 if unreadable 141// max_centi : the load ceiling above which width is squeezed 142// wmin/wmax : hard clamps -- wmin keeps a pool USEFUL (a 0-width pool is a dead feature, not a 143// throttle), wmax keeps one instance from eating the whole budget when it is alone. 144// 145// SQUEEZE IS PROPORTIONAL, not a cliff: above the ceiling, width scales by max/load, so a box at 2x the 146// ceiling gets half the slots. A step function would oscillate (all-or-nothing pools thrash between 147// saturation and idle); proportional control degrades smoothly. 148// FAIL-SAFE: an unreadable load yields wmin. Not knowing the pressure is not permission to grab slots. 149func pw_width(total_budget: i64, n_instances: i64, load_centi: i64, 150 max_centi: i64, wmin: i64, wmax: i64) -> i64 { 151 if wmin < 1 { return 0 - 1 } 152 if wmax < wmin { return 0 - 1 } 153 if total_budget < 1 { return wmin } 154 if max_centi < 1 { return wmin } 155 if load_centi == PW_UNKNOWN_LOAD { return wmin } 156 if load_centi < 0 { return wmin } 157 var n: i64 = n_instances 158 if n < 1 { n = 1 } 159 var w: i64 = total_budget / n 160 if load_centi > max_centi { w = (w * max_centi) / load_centi } 161 if w < wmin { w = wmin } 162 if w > wmax { w = wmax } 163 return w 164} 165 166// The aggregate this policy actually admits -- the number that was never bounded before. 167// A caller can assert this against its budget; per-instance reasoning cannot (that IS the defect). 168func pw_aggregate(width: i64, n_instances: i64) -> i64 { 169 if width < 0 { return 0 - 1 } 170 var n: i64 = n_instances 171 if n < 1 { n = 1 } 172 return width * n 173} 174 175// Does this policy hold the ecosystem inside its budget? 1 = yes, 0 = OVERCOMMITTED. 176// NOTE the wmin floor can legitimately overshoot: keeping N pools USABLE may exceed the budget, and that 177// is a deliberate, VISIBLE trade (a 0-width pool is a broken feature). This predicate exposes it rather 178// than letting it hide -- the caller decides whether to admit fewer INSTANCES instead of thinner pools. 179func pw_within_budget(width: i64, n_instances: i64, total_budget: i64) -> i64 { 180 let agg: i64 = pw_aggregate(width, n_instances) 181 if agg < 0 { return 0 } 182 if agg > total_budget { return 0 } 183 return 1 184}