nx_sc_scope_gate.nx
buildroot/runtime/nx_sc_scope_gate.nx
about
nx_sc_scope_gate.nx -- the referee for LR2, structured concurrency: scoped spawn.
SUBJECT: sc_scope_spawn and its scope verbs in runtime/nx_thread_pool.nx.
WHY THIS GATE IS BUILT THE WAY IT IS. A concurrency gate is the easiest kind to
write vacuously: if the child finishes before the parent looks, EVERY assertion
passes and NOTHING about joining has been tested. So the join teeth here do not
ask "did the children finish" -- that is true of a no-op join too. They ask
three questions a no-op join cannot answer yes to:
* were the children observably INSIDE their bodies, and observably NOT
finished, at the instant the parent was about to join (the fixture is
asserted to have REACHED the condition before any outcome is asserted);
* did join return STRICTLY AFTER every child completed, proved by a shared
monotonic sequence counter rather than by a millisecond clock that can tie;
* did join BLOCK for at least the interval the children were measured holding.
A no-op join returns in microseconds while the children hold for witness-hold-ms,
so the third tooth alone separates them, and the second proves the ordering exactly.
NO FIXTURE FILES. This gate builds its whole fixture at RUNTIME out of mmapped
words and pool tasks, so it shares no path with any production beat and needs no
/tmp scratch directory to create or clean up.
EVERY THRESHOLD IS A CONF ROW (knowledge/sc_scope.conf). The two exceptions are
the join guard and the join spin, which carry NO row on purpose because they are
DERIVED from nx_pool_wait -- and one of the teeth below proves the conf really
does not carry them, so the derivation cannot be quietly unpinned.
expect_exit: 0 license_tier: ORIGINAL lineage_id: sc_scope_gate_v1
dependencies 6 imports · 0 importers
imports: nx_syscalls.nxnx_atom.nxnx_hw.nxnx_thread_pool.nxnx_gate_verdict.nxnx_lineconf_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 36 | const SG_ERRCODE: i64 = 424242 // a child return nothing else could produce |
| 37 | const SG_MAXKIDS: i64 = 256 // BOUND on the witness arrays, named not silent |
| 43 | const SG_STARTWAIT_GUARD: i64 = 2000000 |
| 44 | const SG_ASCII_ZERO: i64 = 48 |
| 45 | const SG_ASCII_NINE: i64 = 57 |
| 46 | const SG_ASCII_DOT: i64 = 46 |
| 47 | const SG_CENTI: i64 = 100 |
| 48 | const SG_PERMIL: i64 = 1000 |
| 52 | const SG_SINK_MOD: i64 = 1024 |
| 53 | const SG_WORD: i64 = 8 |
functions
| 64 | func sg_conf(key: *u8) -> i64 { return lcf_int_of("knowledge/sc_scope.conf" as *u8, key) } |
| 67 | func sg_load1_centi() -> i64 |
| 109 | func _sg_spin(n: i64) -> i64 |
| 130 | func _sg_hold_task(ctx: i64) -> i64 |
| 139 | func _sg_ok_task(ctx: i64) -> i64 { return 0 } |
| 140 | func _sg_err_task(ctx: i64) -> i64 { return SG_ERRCODE } |
| 142 | func _sg_gate_task(ctx: i64) -> i64 |
| 147 | func _sg_work_task(ctx: i64) -> i64 |
| 155 | func sg_kv(k: *u8, v: i64) -> i64 { gv_puts(k); gv_num(v); gv_puts("\n" as *u8); return 0 } |
| 157 | func main() -> i64 |