findings(E-5,E-8,E-9): module-identity ngspice events + runWaitCompletion admission gate
E-8 (re-implemented for JSPI — the codex gate is entangled with the dropped
execution owner; under JSPI a fresh non-suspending JS→wasm entry while
another activation is suspended is structurally safe on its own stack, so
the admission boundary for worker completions is liveness + trap state, not
execution ownership):
- jspi-scheduler.js grows `terminal` (trapped instance; distinct from `dead`),
canTouchNative(), _terminalizeNativeTrap() (WebAssembly.RuntimeError +
cross-realm string classification), and runWaitCompletion(site, token,
prepare, inertResult): prepare runs immediately and owns ALL native work;
stale tokens and dead/terminal instances drop loudly without resolving
(resolving would resume the parked frame inside the damaged module); a
trap latches terminal; a plain JS bug resolves inertResult so the wait
fails instead of stranding. beginWait refuses (token 0) when dead/terminal.
- all four delayed completion sites route their native work through the
gate: 'OCC export completion' (exporter_step_stub), 'OCC model completion'
(oce_plugin_stub — the MEMFS cache write moves inside the gate too),
'ngspice request completion' and 'ngspice vector completion'
(sharedspice_client — every HEAP32/HEAPF64/malloc write inside prepare,
inertResult 1 = transport error). Every wxWasmBeginWait caller in the
stubs bails on token <= 0.
- deliberately NOT ported from codex: ownerModule, enqueueNativeCompletion,
executionBarrier, the byte-credit native-entry FIFO — completions are
one-shot per wait token and stream volume is bounded at the E-6 transport
credit window. Cross-refs logged for group M (M-2/M-6/M-8).
E-5 (re-implemented; codex shape kept, owner APIs replaced with the E-8
gate): js_ngspice_install_events binds the handler to the EXACT installing
module (handler.__pcbjamNgspiceOwnerModule stamp; presence is not identity),
re-installation replaces a foreign module's handler, a superseded handler
disarms itself, native entry goes through installingModule._malloc/
._pcbjam_ngspice_event (never lexical Module), each dispatch checks
canTouchNative() (loud drop on a dead/terminal module), and a trap on the
per-line entry latches the terminal gate.
Tests: scheduler-shim.test.ts +7 (gate happy/stale/dead/terminal/cross-realm/
js-bug/beginWait-refusal). e2e specs updated from the codex line: occ-export
decode-fault recovery (real onmessageerror transition via failDecode, J-4),
ngspice-probe direct-service coverage, eeschema-sim rewritten onto the E-7
applied-generation receipt (codex's executionBarrier await replaced with a
pendingWaits('ngspice') drain poll — the JSPI-line equivalent).
Also bumps the kicad submodule to the E-7/E-9 commit (dd5751038f7).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0b8e7186d4
commit
c14e76651c
9 changed files with 763 additions and 149 deletions
|
|
@ -67,11 +67,18 @@ EM_JS( void, js_occLoadModelStart, ( int aToken, const char* aModelPath ),
|
|||
{
|
||||
const modelPath = UTF8ToString( aModelPath );
|
||||
|
||||
const finish = ( cachePath ) => {
|
||||
const n = lengthBytesUTF8( cachePath ) + 1;
|
||||
const p = _malloc( n );
|
||||
stringToUTF8( cachePath, p, n );
|
||||
globalThis.__wxScheduler.resolveWait( aToken, p );
|
||||
// E-8: all native work (the MEMFS cache write + the malloc'd path) runs
|
||||
// inside the scheduler's completion gate — a dead or trapped instance
|
||||
// drops the completion loudly instead of re-entering wasm, and a trap
|
||||
// inside the prepare latches the instance terminal without resolving.
|
||||
const finish = ( writeCache ) => {
|
||||
globalThis.__wxScheduler.runWaitCompletion( 'OCC model completion', aToken, () => {
|
||||
const cachePath = writeCache();
|
||||
const n = lengthBytesUTF8( cachePath ) + 1;
|
||||
const p = _malloc( n );
|
||||
stringToUTF8( cachePath, p, n );
|
||||
return p;
|
||||
} );
|
||||
};
|
||||
|
||||
let req;
|
||||
|
|
@ -100,22 +107,26 @@ EM_JS( void, js_occLoadModelStart, ( int aToken, const char* aModelPath ),
|
|||
}
|
||||
|
||||
req.then( ( res ) => {
|
||||
let cachePath = '';
|
||||
finish( () => {
|
||||
let cachePath = '';
|
||||
|
||||
if( res && res.ok && res.bytes && res.bytes.length )
|
||||
{
|
||||
cachePath = '/tmp/pcbjam_occ_model_cache.3dc';
|
||||
FS.writeFile( cachePath, res.bytes );
|
||||
}
|
||||
else if( res && res.report )
|
||||
{
|
||||
console.error( '[pcbjam-occ] loadModel failed:', res.report );
|
||||
}
|
||||
if( res && res.ok && res.bytes && res.bytes.length )
|
||||
{
|
||||
cachePath = '/tmp/pcbjam_occ_model_cache.3dc';
|
||||
FS.writeFile( cachePath, res.bytes );
|
||||
}
|
||||
else if( res && res.report )
|
||||
{
|
||||
console.error( '[pcbjam-occ] loadModel failed:', res.report );
|
||||
}
|
||||
|
||||
finish( cachePath );
|
||||
return cachePath;
|
||||
} );
|
||||
} ).catch( ( e ) => {
|
||||
// The gate makes this fallback inert after a trap or shutdown — it
|
||||
// cannot repeat native work in a damaged instance.
|
||||
console.error( '[pcbjam-occ] loadModel request failed:', e );
|
||||
finish( '' );
|
||||
finish( () => '' );
|
||||
} );
|
||||
} )
|
||||
|
||||
|
|
@ -225,6 +236,11 @@ SCENEGRAPH* oce3d_Load( char const* aFileName )
|
|||
return nullptr;
|
||||
|
||||
const int token = wxWasmBeginWait( "occ" );
|
||||
|
||||
// Token 0 = the scheduler refused the wait (dead or terminal instance).
|
||||
if( token <= 0 )
|
||||
return nullptr;
|
||||
|
||||
js_occLoadModelStart( token, aFileName );
|
||||
|
||||
// The malloc'd path pointer rides the wait as an int32.
|
||||
|
|
|
|||
Loading…
Reference in a new issue