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}