code wiki / (root) / nx_sc_scope_gate.nx

nx_sc_scope_gate.nx source

↩ module page · 517 lines · 27386 B

1// nx_sc_scope_gate.nx -- the referee for LR2, structured concurrency: scoped spawn. 2// 3// SUBJECT: sc_scope_spawn and its scope verbs in runtime/nx_thread_pool.nx. 4// 5// WHY THIS GATE IS BUILT THE WAY IT IS. A concurrency gate is the easiest kind to 6// write vacuously: if the child finishes before the parent looks, EVERY assertion 7// passes and NOTHING about joining has been tested. So the join teeth here do not 8// ask "did the children finish" -- that is true of a no-op join too. They ask 9// three questions a no-op join cannot answer yes to: 10// * were the children observably INSIDE their bodies, and observably NOT 11// finished, at the instant the parent was about to join (the fixture is 12// asserted to have REACHED the condition before any outcome is asserted); 13// * did join return STRICTLY AFTER every child completed, proved by a shared 14// monotonic sequence counter rather than by a millisecond clock that can tie; 15// * did join BLOCK for at least the interval the children were measured holding. 16// A no-op join returns in microseconds while the children hold for witness-hold-ms, 17// so the third tooth alone separates them, and the second proves the ordering exactly. 18// 19// NO FIXTURE FILES. This gate builds its whole fixture at RUNTIME out of mmapped 20// words and pool tasks, so it shares no path with any production beat and needs no 21// /tmp scratch directory to create or clean up. 22// 23// EVERY THRESHOLD IS A CONF ROW (knowledge/sc_scope.conf). The two exceptions are 24// the join guard and the join spin, which carry NO row on purpose because they are 25// DERIVED from nx_pool_wait -- and one of the teeth below proves the conf really 26// does not carry them, so the derivation cannot be quietly unpinned. 27// 28// expect_exit: 0 license_tier: ORIGINAL lineage_id: sc_scope_gate_v1 29import "nx_syscalls.nx" 30import "nx_atom.nx" 31import "nx_hw.nx" 32import "nx_thread_pool.nx" 33import "nx_gate_verdict.nx" 34import "nx_lineconf_lib.nx" 35 36const SG_ERRCODE: i64 = 424242 // a child return nothing else could produce 37const SG_MAXKIDS: i64 = 256 // BOUND on the witness arrays, named not silent 38// Bound on the fixture's start-wait, in yields. A yield is roughly a microsecond, 39// so this is about two seconds. It is deliberately SHORT: a fixture that has not 40// got every child into its body within two seconds has NOT reached its condition, 41// and the right outcome then is a FAILING tooth that says so, never a gate that 42// hangs for minutes and reads as a dead organ to whoever called it. 43const SG_STARTWAIT_GUARD: i64 = 2000000 44const SG_ASCII_ZERO: i64 = 48 45const SG_ASCII_NINE: i64 = 57 46const SG_ASCII_DOT: i64 = 46 47const SG_CENTI: i64 = 100 48const SG_PERMIL: i64 = 1000 49// bounds the shared sink accumulator so a long spin cannot overflow it. Named for 50// what it DOES, not for what it is: nx_magic proposed SG_MAGIC_1024 and says itself 51// that renaming to a domain term is the owner judgment rule 11 is asking for. 52const SG_SINK_MOD: i64 = 1024 53const SG_WORD: i64 = 8 54 55static g_sg_start: *i64 // [i] = 1 once child i is inside its body 56static g_sg_done: *i64 // [i] = sequence stamp at completion, 0 = never completed 57static g_sg_rel: *i64 // [0] release word for the T1 witnesses 58static g_sg_rel2: *i64 // [0] release word for the T3 cancellation fixture 59static g_sg_seq: *i64 // [0] shared monotonic sequence -- the join ordering oracle 60static g_sg_sink: *i64 // [0] bench accumulator, so the bench work is not dead code 61static g_sg_work: i64 // bench spin iterations (conf) 62static g_sg_holditers: i64 // CALIBRATED spin count a witness child holds for 63 64func sg_conf(key: *u8) -> i64 { return lcf_int_of("knowledge/sc_scope.conf" as *u8, key) } 65 66// first field of /proc/loadavg as centi-load ("2.15" -> 215); -1 when unreadable. 67func sg_load1_centi() -> i64 { 68 let lp: *i64 = sys_mmap(SG_WORD * 2) as *i64 69 let b: *u8 = sys_read_file("/proc/loadavg" as *u8, lp) 70 if (b as i64) == 0 { return 0 - 1 } 71 let n: i64 = lp[0] 72 var i: i64 = 0 73 var whole: i64 = 0 74 var wd: i64 = 0 75 while i < n { 76 let c: i64 = b[i] as i64 77 if c < SG_ASCII_ZERO { break } 78 if c > SG_ASCII_NINE { break } 79 whole = whole * 10 + (c - SG_ASCII_ZERO) 80 wd = wd + 1 81 i = i + 1 82 } 83 if wd == 0 { return 0 - 1 } 84 var frac: i64 = 0 85 var got: i64 = 0 86 if i < n { 87 let dot: i64 = b[i] as i64 88 if dot == SG_ASCII_DOT { 89 i = i + 1 90 while got < 2 { 91 if i >= n { break } 92 let c2: i64 = b[i] as i64 93 if c2 < SG_ASCII_ZERO { break } 94 if c2 > SG_ASCII_NINE { break } 95 frac = frac * 10 + (c2 - SG_ASCII_ZERO) 96 got = got + 1 97 i = i + 1 98 } 99 } 100 } 101 while got < 2 { frac = frac * 10; got = got + 1 } 102 return whole * SG_CENTI + frac 103} 104 105// ---- fixture tasks (all plain COLORLESS func(i64) -> i64) -------------------- 106 107// PURE ARITHMETIC, NO SYSCALL, NO ALLOCATION. Used both for the parent-side 108// calibration and, at the calibrated count, for the child hold. 109func _sg_spin(n: i64) -> i64 { 110 var s: i64 = 0 111 var k: i64 = 0 112 while k < n { s = s + (k % 7) * 3; k = k + 1 } 113 return s 114} 115 116// Announces it is running, waits for release, holds for the CALIBRATED interval, 117// then stamps a monotonic sequence number. The stamp is q+1 so that 0 means "never". 118// 119// WHY THE HOLD IS A SPIN AND NOT A CLOCK -- MEASURED 2026-08-25, IT CRASHED THIS 120// GATE ONCE ALREADY. sys_now_ms allocates its timespec with sys_mmap(16); a small 121// request comes from nx_syscalls' bump arena; and that arena advances its cursor 122// with a PLAIN non-atomic read-modify-write on nxa_st[0]. Eight pool workers 123// calling sys_now_ms in a spin loop therefore raced the cursor, handed two threads 124// overlapping 16-byte allocations, tripped the ring canary (ARENA-OVERRUN 125// prev_alloc_size=16) and ended in SIGSEGV. The hazard is general and it is not 126// this scope's: ANY small sys_mmap is unsafe from more than one thread at a time, 127// which puts sys_now_ms, sys_now_us and lcf_int_of off limits inside a pool task. 128// It is also why _sc_kid_main allocates nothing -- that is a load-bearing property 129// of the scope, not an incidental one. 130func _sg_hold_task(ctx: i64) -> i64 { 131 nx_atom_store_i64(((g_sg_start as i64) + ctx * SG_WORD) as *i64, 1, NX_MO_SEQ_CST) 132 while nx_atom_load_i64(g_sg_rel, NX_MO_SEQ_CST) == 0 { nx_thread_yield() } 133 let s: i64 = _sg_spin(g_sg_holditers) 134 nx_atom_faa_i64(g_sg_sink, s % SG_SINK_MOD, NX_MO_SEQ_CST) 135 let q: i64 = nx_atom_faa_i64(g_sg_seq, 1, NX_MO_SEQ_CST) 136 nx_atom_store_i64(((g_sg_done as i64) + ctx * SG_WORD) as *i64, q + 1, NX_MO_SEQ_CST) 137 return 0 138} 139func _sg_ok_task(ctx: i64) -> i64 { return 0 } 140func _sg_err_task(ctx: i64) -> i64 { return SG_ERRCODE } 141// occupies a worker until rel2, so the rest of its scope is still QUEUED at cancel time 142func _sg_gate_task(ctx: i64) -> i64 { 143 while nx_atom_load_i64(g_sg_rel2, NX_MO_SEQ_CST) == 0 { nx_thread_yield() } 144 return 0 145} 146// real arithmetic, so the bench arms measure the pool and not the dispatch alone 147func _sg_work_task(ctx: i64) -> i64 { 148 var s: i64 = 0 149 var i: i64 = 0 150 while i < g_sg_work { s = s + (i % 7) * 3 + ctx; i = i + 1 } 151 nx_atom_faa_i64(g_sg_sink, s % SG_SINK_MOD, NX_MO_SEQ_CST) 152 return 0 153} 154 155func sg_kv(k: *u8, v: i64) -> i64 { gv_puts(k); gv_num(v); gv_puts("\n" as *u8); return 0 } 156 157func main() -> i64 { 158 let ctr: *i64 = gv_ctr() 159 gv_head("=== NX-SC-SCOPE-GATE -- LR2 structured concurrency: scoped spawn over the colorless pool ===" as *u8) 160 161 // ---- conf: a missing row REFUSES, it never defaults ---------------------- 162 let cap: i64 = sg_conf("max-children-per-scope" as *u8) 163 let hold_ms: i64 = sg_conf("witness-hold-ms" as *u8) 164 let ovh_max: i64 = sg_conf("scope-overhead-max-permil" as *u8) 165 let sp_floor: i64 = sg_conf("pool-speedup-floor-permil" as *u8) 166 let bench_n: i64 = sg_conf("bench-tasks" as *u8) 167 let bench_w: i64 = sg_conf("bench-work-iters" as *u8) 168 let cal_iters: i64 = sg_conf("witness-calibration-iters" as *u8) 169 let hold_acc: i64 = sg_conf("witness-hold-accept-permil" as *u8) 170 var conf_ok: i64 = 1 171 if cal_iters == LCF_MISS { conf_ok = 0 } 172 if hold_acc == LCF_MISS { conf_ok = 0 } 173 if cap == LCF_MISS { conf_ok = 0 } 174 if hold_ms == LCF_MISS { conf_ok = 0 } 175 if ovh_max == LCF_MISS { conf_ok = 0 } 176 if sp_floor == LCF_MISS { conf_ok = 0 } 177 if bench_n == LCF_MISS { conf_ok = 0 } 178 if bench_w == LCF_MISS { conf_ok = 0 } 179 if gv_need("knowledge/sc_scope.conf carries every declared row" as *u8, conf_ok, ctr) == 0 { 180 return gv_verdict("SC-SCOPE-GATE" as *u8, ctr, "subject sc_scope_spawn -- strength is in each check name" as *u8) 181 } 182 g_sg_work = bench_w 183 184 // CALIBRATE the child hold, single-threaded, before any worker exists. The spin 185 // count is DERIVED from this host's measured rate rather than picked for it, so 186 // the witness stays decisive on a fast box and on a slow one. 187 let cal0: i64 = sys_now_ms() 188 let cal_sink: i64 = _sg_spin(cal_iters) 189 var cal_ms: i64 = sys_now_ms() - cal0 190 if cal_ms < 1 { cal_ms = 1 } // divide-by-zero guard, not a tunable 191 g_sg_holditers = cal_iters * hold_ms / cal_ms 192 let hold_floor_ms: i64 = hold_ms * hold_acc / SG_PERMIL 193 194 let pool: *NxThreadPool = nx_pool_new(0, 64) 195 var nw: i64 = pool.n_workers 196 if nw > SG_MAXKIDS { nw = SG_MAXKIDS } 197 let l1: i64 = sg_load1_centi() 198 let ncpu: i64 = nx_hw_worker_count() 199 sg_kv(" workers=" as *u8, pool.n_workers) 200 sg_kv(" witnesses=" as *u8, nw) 201 sg_kv(" ncpu=" as *u8, ncpu) 202 sg_kv(" load1_centi=" as *u8, l1) 203 sg_kv(" conf max-children-per-scope=" as *u8, cap) 204 sg_kv(" conf witness-hold-ms=" as *u8, hold_ms) 205 sg_kv(" calibration iters=" as *u8, cal_iters) 206 sg_kv(" calibration ms=" as *u8, cal_ms) 207 sg_kv(" calibration sink (proves the spin was not elided)=" as *u8, cal_sink % SG_SINK_MOD) 208 sg_kv(" DERIVED child hold iters=" as *u8, g_sg_holditers) 209 sg_kv(" join must be observed blocking at least ms=" as *u8, hold_floor_ms) 210 gv_puts(" join-guard-iters=" as *u8); gv_num(sc_join_guard_iters()) 211 gv_puts(" join-spin=" as *u8); gv_num(sc_join_spin()) 212 gv_puts(" provenance=DERIVED source=nx_pool_wait (no conf row exists for either)\n" as *u8) 213 gv_puts(" bytes-per-scope-mapping=" as *u8); gv_num(SC_SCOPE_BYTES + cap * SC_KID_BYTES) 214 gv_puts(" bytes-per-spawn=" as *u8); gv_num(SC_KID_BYTES) 215 gv_puts(" bump-arena-threshold=" as *u8); gv_num(NXA_SMALL_MAX); gv_puts("\n" as *u8) 216 217 if gv_need("pool has at least two workers (a one-worker pool cannot witness a join)" as *u8, ((nw > 1) as i64), ctr) == 0 { 218 return gv_verdict("SC-SCOPE-GATE" as *u8, ctr, "subject sc_scope_spawn -- strength is in each check name" as *u8) 219 } 220 221 g_sg_start = sys_mmap(SG_MAXKIDS * SG_WORD) as *i64 222 g_sg_done = sys_mmap(SG_MAXKIDS * SG_WORD) as *i64 223 g_sg_rel = sys_mmap(SG_WORD * 2) as *i64 224 g_sg_rel2 = sys_mmap(SG_WORD * 2) as *i64 225 g_sg_seq = sys_mmap(SG_WORD * 2) as *i64 226 g_sg_sink = sys_mmap(SG_WORD * 2) as *i64 227 g_sg_rel[0] = 0 228 g_sg_rel2[0] = 0 229 g_sg_seq[0] = 0 230 g_sg_sink[0] = 0 231 var i: i64 = 0 232 while i < SG_MAXKIDS { g_sg_start[i] = 0; g_sg_done[i] = 0; i = i + 1 } 233 234 // ================= T1: DOES THE JOIN ACTUALLY WAIT ========================= 235 let scA: *NxScope = sc_scope_open(pool) 236 var openedA: i64 = 0 237 if (scA as i64) != 0 { openedA = 1 } 238 if gv_need("sc_scope_open returns a scope" as *u8, openedA, ctr) == 0 { 239 sg_kv(" sc_last_refusal=" as *u8, sc_last_refusal()) 240 return gv_verdict("SC-SCOPE-GATE" as *u8, ctr, "subject sc_scope_spawn -- strength is in each check name" as *u8) 241 } 242 i = 0 243 while i < nw { sc_scope_spawn(scA, _sg_hold_task, i); i = i + 1 } 244 245 // ASSERT THE FIXTURE REACHED THE CONDITION BEFORE ASSERTING ANY OUTCOME: 246 // spin until every witness is observably inside its body. 247 var started: i64 = 0 248 var guard: i64 = 0 249 while started < nw { 250 started = 0 251 i = 0 252 while i < nw { 253 if nx_atom_load_i64(((g_sg_start as i64) + i * SG_WORD) as *i64, NX_MO_SEQ_CST) == 1 { started = started + 1 } 254 i = i + 1 255 } 256 nx_thread_yield() 257 guard = guard + 1 258 if guard > SG_STARTWAIT_GUARD { break } 259 } 260 var done_early: i64 = 0 261 i = 0 262 while i < nw { 263 if nx_atom_load_i64(((g_sg_done as i64) + i * SG_WORD) as *i64, NX_MO_SEQ_CST) != 0 { done_early = done_early + 1 } 264 i = i + 1 265 } 266 let probe_running: i64 = sc_scope_is_joined(scA) // the BAD input for the bite 267 let t_pre: i64 = nx_atom_faa_i64(g_sg_seq, 1, NX_MO_SEQ_CST) + 1 268 let ms_rel: i64 = sys_now_ms() 269 nx_atom_store_i64(g_sg_rel, 1, NX_MO_SEQ_CST) 270 let joinA: i64 = sc_scope_join(scA) 271 let ms_join: i64 = sys_now_ms() 272 let t_join: i64 = nx_atom_faa_i64(g_sg_seq, 1, NX_MO_SEQ_CST) + 1 273 let probe_after: i64 = sc_scope_is_joined(scA) // the GOOD input for the bite 274 let elapsed: i64 = ms_join - ms_rel 275 276 var all_before_join: i64 = 1 277 var all_after_pre: i64 = 1 278 var all_state_done: i64 = 1 279 i = 0 280 while i < nw { 281 let d: i64 = nx_atom_load_i64(((g_sg_done as i64) + i * SG_WORD) as *i64, NX_MO_SEQ_CST) 282 if d == 0 { all_before_join = 0 } 283 if d >= t_join { all_before_join = 0 } 284 if d <= t_pre { all_after_pre = 0 } 285 if sc_scope_kid_state(scA, i) != SC_KID_DONE { all_state_done = 0 } 286 i = i + 1 287 } 288 sg_kv(" T1 children observed running before join=" as *u8, started) 289 sg_kv(" T1 children already finished before join=" as *u8, done_early) 290 sg_kv(" T1 join blocked ms=" as *u8, elapsed) 291 sg_kv(" T1 seq pre-join=" as *u8, t_pre) 292 sg_kv(" T1 seq post-join=" as *u8, t_join) 293 294 let t1a: i64 = ((started == nw) as i64) & ((done_early == 0) as i64) & ((nw > 0) as i64) 295 gv_check("fixture-every-child-observably-inside-its-body-and-none-finished-before-join" as *u8, t1a, ctr) 296 let t1b: i64 = ((nw > 0) as i64) & all_before_join & all_after_pre 297 gv_check("join-returns-strictly-after-every-child-completed-proved-by-sequence-order" as *u8, t1b, ctr) 298 let t1c: i64 = ((nw > 0) as i64) & ((elapsed >= hold_floor_ms) as i64) & ((hold_floor_ms > 0) as i64) 299 gv_check("join-blocked-at-least-the-calibrated-child-hold-which-a-no-op-join-cannot" as *u8, t1c, ctr) 300 let t1d: i64 = ((sc_scope_spawned(scA) == nw) as i64) & ((sc_scope_finished(scA) == nw) as i64) & ((nw > 0) as i64) 301 gv_check("scope-reports-finished-equals-spawned-with-a-nonzero-denominator" as *u8, t1d, ctr) 302 let t1e: i64 = ((nw > 0) as i64) & all_state_done 303 gv_check("every-child-record-reports-DONE-not-merely-the-aggregate-counter" as *u8, t1e, ctr) 304 gv_check("join-returns-zero-when-no-child-errored" as *u8, ((joinA == 0) as i64), ctr) 305 gv_bite("neg-control-scope-with-a-still-running-child-must-not-report-joined" as *u8, ((probe_running == 0) as i64), ((probe_after == 0) as i64), ctr) 306 307 // ================= T2: A CHILD ERROR PROPAGATES TO THE SCOPE =============== 308 let scB: *NxScope = sc_scope_open(pool) 309 sc_scope_spawn(scB, _sg_ok_task, 0) 310 sc_scope_spawn(scB, _sg_ok_task, 1) 311 sc_scope_spawn(scB, _sg_err_task, 2) 312 sc_scope_spawn(scB, _sg_ok_task, 3) 313 let joinB: i64 = sc_scope_join(scB) 314 sg_kv(" T2 join returned=" as *u8, joinB) 315 sg_kv(" T2 scope err=" as *u8, sc_scope_err(scB)) 316 sg_kv(" T2 scope err_child=" as *u8, sc_scope_err_child(scB)) 317 let t2a: i64 = ((joinB == SG_ERRCODE) as i64) & ((sc_scope_err(scB) == SG_ERRCODE) as i64) 318 gv_check("child-error-propagates-to-the-scope-and-join-hands-it-back" as *u8, t2a, ctr) 319 gv_check("scope-names-WHICH-child-errored-not-merely-that-one-did" as *u8, ((sc_scope_err_child(scB) == 2) as i64), ctr) 320 let t2c: i64 = ((sc_scope_spawned(scB) == 4) as i64) & ((sc_scope_finished(scB) == 4) as i64) 321 gv_check("scope-still-joins-every-child-after-an-error-no-child-is-abandoned" as *u8, t2c, ctr) 322 gv_check("an-error-cancels-its-OWN-scope-so-unstarted-siblings-stop" as *u8, ((sc_scope_cancelled(scB) == 1) as i64), ctr) 323 sc_scope_free(scB) 324 325 // ================= T3: CANCELLATION IS SCOPED ============================= 326 let scC: *NxScope = sc_scope_open(pool) // the cancelled scope 327 let scD: *NxScope = sc_scope_open(pool) // its live sibling, open across the cancel 328 let manyC: i64 = nw * 4 329 i = 0 330 while i < manyC { sc_scope_spawn(scC, _sg_gate_task, i); i = i + 1 } 331 i = 0 332 while i < nw { sc_scope_spawn(scD, _sg_ok_task, i); i = i + 1 } 333 sc_scope_cancel(scC) 334 let cancD_at_cancel: i64 = sc_scope_cancelled(scD) 335 nx_atom_store_i64(g_sg_rel2, 1, NX_MO_SEQ_CST) 336 let joinC: i64 = sc_scope_join(scC) 337 let joinD: i64 = sc_scope_join(scD) 338 var skipC: i64 = 0 339 var doneC: i64 = 0 340 i = 0 341 while i < manyC { 342 let s: i64 = sc_scope_kid_state(scC, i) 343 if s == SC_KID_SKIPPED { skipC = skipC + 1 } 344 if s == SC_KID_DONE { doneC = doneC + 1 } 345 i = i + 1 346 } 347 var skipD: i64 = 0 348 var doneD: i64 = 0 349 i = 0 350 while i < nw { 351 let s2: i64 = sc_scope_kid_state(scD, i) 352 if s2 == SC_KID_SKIPPED { skipD = skipD + 1 } 353 if s2 == SC_KID_DONE { doneD = doneD + 1 } 354 i = i + 1 355 } 356 sg_kv(" T3 cancelled-scope children=" as *u8, manyC) 357 sg_kv(" T3 cancelled-scope skipped=" as *u8, skipC) 358 sg_kv(" T3 cancelled-scope ran=" as *u8, doneC) 359 sg_kv(" T3 sibling-scope children=" as *u8, nw) 360 sg_kv(" T3 sibling-scope skipped=" as *u8, skipD) 361 sg_kv(" T3 sibling-scope ran=" as *u8, doneD) 362 gv_check("fixture-cancel-landed-while-children-were-still-queued-skipped-is-nonzero" as *u8, ((skipC > 0) as i64), ctr) 363 let t3b: i64 = ((skipC + doneC == manyC) as i64) & ((manyC > 0) as i64) 364 gv_check("every-child-of-a-cancelled-scope-is-accounted-skipped-plus-ran-equals-spawned" as *u8, t3b, ctr) 365 let t3c: i64 = ((skipD == 0) as i64) & ((doneD == nw) as i64) & ((nw > 0) as i64) 366 gv_check("cancellation-is-SCOPED-the-sibling-scope-loses-not-one-child" as *u8, t3c, ctr) 367 let t3d: i64 = ((cancD_at_cancel == 0) as i64) & ((sc_scope_cancelled(scD) == 0) as i64) & ((sc_scope_cancelled(scC) == 1) as i64) 368 gv_check("neg-control-sibling-cancel-flag-stays-clear-while-the-target-flag-is-set" as *u8, t3d, ctr) 369 let t3e: i64 = ((joinC == 0) as i64) & ((joinD == 0) as i64) 370 gv_check("a-cancelled-scope-still-joins-cleanly-rather-than-tripping-its-guard" as *u8, t3e, ctr) 371 sc_scope_free(scC) 372 sc_scope_free(scD) 373 374 // ================= T4: THE ORPHAN SHAPE IS REFUSED BY NAME ================= 375 let r_closed: i64 = sc_scope_spawn(scA, _sg_ok_task, 0) // scA was joined in T1 376 let r_null: i64 = sc_scope_spawn(0 as *NxScope, _sg_ok_task, 0) 377 let r_join2: i64 = sc_scope_join(scA) // double join 378 let scE: *NxScope = sc_scope_open(pool) 379 let r_freeopen: i64 = sc_scope_free(scE) // free while OPEN 380 381 let sub0: i64 = pool.tasks_submitted 382 sc_strict_set(1) 383 let r_strict: i64 = nx_pool_submit(pool, _sg_ok_task, 0) 384 let sub1: i64 = pool.tasks_submitted 385 i = 0 386 while i < 2 { sc_scope_spawn(scE, _sg_ok_task, i); i = i + 1 } 387 let joinE: i64 = sc_scope_join(scE) 388 let strict_scoped: i64 = ((sc_scope_spawned(scE) == 2) as i64) & ((sc_scope_finished(scE) == 2) as i64) 389 sc_strict_set(0) 390 let done_b4: i64 = nx_pool_n_completed(pool) 391 let sub2: i64 = pool.tasks_submitted 392 let r_unstrict: i64 = nx_pool_submit(pool, _sg_ok_task, 0) 393 nx_pool_wait(pool, done_b4 + 1) 394 let sub3: i64 = pool.tasks_submitted 395 396 let scF: *NxScope = sc_scope_open(pool) 397 var okspawn: i64 = 0 398 i = 0 399 while i < cap { 400 if sc_scope_spawn(scF, _sg_ok_task, i) == 0 { okspawn = okspawn + 1 } 401 i = i + 1 402 } 403 let r_full: i64 = sc_scope_spawn(scF, _sg_ok_task, cap) 404 let spawned_cap: i64 = sc_scope_spawned(scF) 405 let joinF: i64 = sc_scope_join(scF) 406 407 sg_kv(" T4 spawn-into-joined-scope=" as *u8, r_closed) 408 sg_kv(" T4 spawn-with-no-scope=" as *u8, r_null) 409 sg_kv(" T4 double-join=" as *u8, r_join2) 410 sg_kv(" T4 free-while-open=" as *u8, r_freeopen) 411 sg_kv(" T4 strict bare submit=" as *u8, r_strict) 412 sg_kv(" T4 pool submitted before strict submit=" as *u8, sub0) 413 sg_kv(" T4 pool submitted after strict submit=" as *u8, sub1) 414 sg_kv(" T4 over-capacity spawn=" as *u8, r_full) 415 sg_kv(" T4 accepted spawns at cap=" as *u8, okspawn) 416 417 gv_check("orphan-shape-refused-spawn-into-an-ALREADY-JOINED-scope-named-CLOSED" as *u8, ((r_closed == SC_REFUSE_CLOSED) as i64), ctr) 418 gv_check("orphan-shape-refused-spawn-with-NO-SCOPE-AT-ALL-named-NO-SCOPE" as *u8, ((r_null == SC_REFUSE_NO_SCOPE) as i64), ctr) 419 gv_check("double-join-refused-by-name-rather-than-silently-succeeding-twice" as *u8, ((r_join2 == SC_REFUSE_CLOSED) as i64), ctr) 420 gv_check("cannot-free-a-scope-whose-children-may-still-be-running" as *u8, ((r_freeopen == SC_REFUSE_CLOSED) as i64), ctr) 421 gv_check("strict-mode-refuses-the-BARE-unscoped-submit-by-name" as *u8, ((r_strict == SC_REFUSE_UNSCOPED) as i64), ctr) 422 gv_check("strict-refusal-QUEUED-NOTHING-asserted-on-the-pool-STATE-not-the-message" as *u8, ((sub1 == sub0) as i64), ctr) 423 let t4g: i64 = strict_scoped & ((joinE == 0) as i64) 424 gv_check("strict-mode-still-admits-the-SCOPED-spawn-so-the-law-is-a-law-not-a-ban" as *u8, t4g, ctr) 425 let t4h: i64 = ((r_unstrict == 0) as i64) & ((sub3 == sub2 + 1) as i64) 426 gv_check("strict-off-restores-the-incumbent-bare-submit-byte-for-byte-contract" as *u8, t4h, ctr) 427 let t4i: i64 = ((r_full == SC_REFUSE_FULL) as i64) & ((spawned_cap == cap) as i64) & ((okspawn == cap) as i64) & ((cap > 0) as i64) 428 gv_check("over-capacity-spawn-refused-by-name-and-the-scope-never-over-counts" as *u8, t4i, ctr) 429 gv_check("a-full-scope-still-joins-every-child-it-did-accept" as *u8, ((joinF == 0) as i64), ctr) 430 sc_scope_free(scE) 431 sc_scope_free(scF) 432 433 // ================= T5: THE JOIN BOUNDS ARE DERIVED, AND UNPINNABLE ======== 434 let conf_guard: i64 = sg_conf("join-guard-iters" as *u8) 435 let conf_spin: i64 = sg_conf("join-spin" as *u8) 436 gv_check("join-guard-and-spin-DERIVED-from-nx_pool_wait-linkage-holds-mechanically" as *u8, ((sc_join_guard_matches_pool() == 1) as i64), ctr) 437 let t5b: i64 = ((conf_guard == LCF_MISS) as i64) & ((conf_spin == LCF_MISS) as i64) 438 gv_check("neg-control-the-conf-carries-NO-row-for-either-so-the-linkage-cannot-be-unpinned" as *u8, t5b, ctr) 439 440 // ================= T6: THE RESOURCE ENVELOPE, MEASURED ==================== 441 let base_bytes: i64 = sc_live_bytes() 442 let scG: *NxScope = sc_scope_open(pool) 443 let held_bytes: i64 = sc_live_bytes() 444 let per_scope: i64 = sc_scope_bytes(scG) 445 let joinG: i64 = sc_scope_join(scG) 446 let freeG: i64 = sc_scope_free(scG) 447 let after_bytes: i64 = sc_live_bytes() 448 sg_kv(" T6 scope bytes held at open=" as *u8, held_bytes - base_bytes) 449 sg_kv(" T6 scope bytes after join and free=" as *u8, after_bytes - base_bytes) 450 let t6a: i64 = ((held_bytes - base_bytes == per_scope) as i64) & ((per_scope > 0) as i64) 451 gv_check("open-accounts-exactly-one-scope-mapping-no-more-and-no-less" as *u8, t6a, ctr) 452 let t6b: i64 = ((after_bytes == base_bytes) as i64) & ((freeG == 0) as i64) & ((joinG == 0) as i64) 453 gv_check("join-then-free-returns-every-byte-the-scope-took-measured-not-asserted" as *u8, t6b, ctr) 454 gv_check("scope-mapping-exceeds-the-bump-arena-threshold-so-munmap-really-returns-it" as *u8, ((per_scope > NXA_SMALL_MAX) as i64), ctr) 455 456 // ================= T7: THE POOL IS NOT REGRESSED ========================== 457 let s0: i64 = sys_now_ms() 458 i = 0 459 while i < bench_n { _sg_work_task(i); i = i + 1 } 460 let t_serial: i64 = sys_now_ms() - s0 461 462 let b0: i64 = sys_now_ms() 463 let d0: i64 = nx_pool_n_completed(pool) 464 i = 0 465 while i < bench_n { nx_pool_submit(pool, _sg_work_task, i); i = i + 1 } 466 nx_pool_wait(pool, d0 + bench_n) 467 let t_bare: i64 = sys_now_ms() - b0 468 469 let c0: i64 = sys_now_ms() 470 let scH: *NxScope = sc_scope_open(pool) 471 i = 0 472 while i < bench_n { sc_scope_spawn(scH, _sg_work_task, i); i = i + 1 } 473 let joinH: i64 = sc_scope_join(scH) 474 sc_scope_free(scH) 475 let t_scoped: i64 = sys_now_ms() - c0 476 477 var speedup_permil: i64 = 0 478 if t_scoped > 0 { speedup_permil = t_serial * SG_PERMIL / t_scoped } 479 var bare_speedup_permil: i64 = 0 480 if t_bare > 0 { bare_speedup_permil = t_serial * SG_PERMIL / t_bare } 481 var ovh_permil: i64 = 0 482 if t_bare > 0 { ovh_permil = (t_scoped - t_bare) * SG_PERMIL / t_bare } 483 sg_kv(" T7 bench tasks=" as *u8, bench_n) 484 sg_kv(" T7 serial ms=" as *u8, t_serial) 485 sg_kv(" T7 bare-pool ms=" as *u8, t_bare) 486 sg_kv(" T7 scoped ms=" as *u8, t_scoped) 487 sg_kv(" T7 bare-pool speedup permil=" as *u8, bare_speedup_permil) 488 sg_kv(" T7 scoped speedup permil=" as *u8, speedup_permil) 489 sg_kv(" T7 scoped overhead over bare permil=" as *u8, ovh_permil) 490 sg_kv(" T7 conf overhead ceiling permil=" as *u8, ovh_max) 491 sg_kv(" T7 conf speedup floor permil=" as *u8, sp_floor) 492 493 let t7a: i64 = ((t_serial > 0) as i64) & ((t_bare > 0) as i64) & ((t_scoped > 0) as i64) 494 gv_check("bench-arms-all-nonzero-so-every-ratio-below-has-a-real-denominator" as *u8, t7a, ctr) 495 let t7b: i64 = ((t_bare > 0) as i64) & ((ovh_permil <= ovh_max) as i64) 496 gv_check("scoped-form-does-not-regress-the-bare-pool-beyond-the-conf-ceiling" as *u8, t7b, ctr) 497 gv_check("the-scoped-bench-arm-completed-every-child-with-no-error" as *u8, ((joinH == 0) as i64), ctr) 498 // THE INCUMBENT IS THE CONTROL, NOT A LOADAVG HEURISTIC. The absolute speedup is 499 // load-dependent, so something must decide whether this box could deliver it at 500 // all -- and the honest judge is the BARE POOL MEASURED IN THIS SAME RUN, on this 501 // same box, in the same second. If the incumbent itself cannot reach the floor, 502 // the box is too saturated for an absolute number to mean anything and the tooth 503 // SKIPs; if the incumbent CAN reach it, the scoped form has no excuse and must. 504 // 505 // A loadavg bar was tried first and was WRONG: it demanded load1 below ncpu on a 506 // shared NAS that sits at 14-21 against 8 CPUs, so the tooth would have been 507 // permanently SKIP -- the detector nobody reads. A control drawn from the same 508 // run cannot go stale that way, and it can never produce a false RED either, 509 // because a saturated box fails the PRECONDITION rather than the check. 510 if gv_need("incumbent bare pool reaches the floor on this box right now (the control)" as *u8, ((bare_speedup_permil >= sp_floor) as i64), ctr) == 1 { 511 let t7d: i64 = ((t_scoped > 0) as i64) & ((speedup_permil >= sp_floor) as i64) 512 gv_check("pooled-speedup-through-a-scope-at-or-above-the-conf-floor" as *u8, t7d, ctr) 513 } 514 515 nx_pool_shutdown(pool) 516 return gv_verdict("SC-SCOPE-GATE" as *u8, ctr, "subject sc_scope_spawn over nx_thread_pool -- each check name carries its own strength" as *u8) 517}