Phase E complete: K4-K6 become token waits — the bridge set is converted, suite at baseline
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2SU74acXyviwSxBhMunFe
This commit is contained in:
parent
75209dc07e
commit
d6c4efcf1f
5 changed files with 190 additions and 86 deletions
|
|
@ -986,6 +986,43 @@ engines, `wakeup_during_transition` layout-sensitive, `modal.spec.ts:125`), with
|
||||||
pthread-ondemand green on both engines. The shim's resolveWait retention change is
|
pthread-ondemand green on both engines. The shim's resolveWait retention change is
|
||||||
regression-free against every modal/nested/popup wait the battery stages.
|
regression-free against every modal/nested/popup wait the battery stages.
|
||||||
|
|
||||||
|
### Phase E: K2–K6 converted — the bridge set is COMPLETE (2026-08-08)
|
||||||
|
|
||||||
|
Same pattern as K1, one build, gated per-bridge then confirmed:
|
||||||
|
|
||||||
|
| bridge | file | wait kind |
|
||||||
|
|---|---|---|
|
||||||
|
| K2 footprint libs | `kicad/pcbnew/pcb_io/pcbjam_fp/pcb_io_pcbjam_fp.cpp` | `fp-lib` |
|
||||||
|
| K3 3D model fetch | `kicad/3d-viewer/3d_cache/pcbjam_model_fetch.cpp` | `3d` |
|
||||||
|
| K4 STEP export | `wasm/stubs/exporter_step_stub.cpp` | `occ` |
|
||||||
|
| K5 OCE model load | `wasm/stubs/oce_plugin_stub.cpp` | `occ` |
|
||||||
|
| K6 ngspice ×2 | `wasm/stubs/sharedspice_client.cpp` | `ngspice` |
|
||||||
|
|
||||||
|
Every bridge: `beginWait` → start `EM_JS` (ALL resolutions deferred ≥ a microtask,
|
||||||
|
result marshalling moved into the resolve callback) → `wxWasmYieldUntil` → result as
|
||||||
|
the wait's int32 (pointers recovered via `(uint32_t)`). Worker/proxy paths untouched.
|
||||||
|
|
||||||
|
**Gates:** `pcbnew` + `3d-viewer-models` + `occ-export-models` + `eeschema-sim`
|
||||||
|
**8/8 in 1.8 min** — and the occ-export spec drove **17 real async provider requests
|
||||||
|
through converted K3 during a live K4 export**, which is the resolve-AFTER-park
|
||||||
|
coverage the K1 retry noted as thin (test pages resolve early; these two specs
|
||||||
|
install real providers). Full suite: **139 / 1 (pre-existing occ-probe glb) / 30 —
|
||||||
|
baseline held.** Battery not rerun: no wx or shim code changed; the stubs link only
|
||||||
|
into the KiCad apps.
|
||||||
|
|
||||||
|
**Converter-diet hardening (K1+K2):** `sym_convert` keeps `sch_io/` and
|
||||||
|
`pcb_convert` keeps `pcb_io/`, so those TUs can land in links WITHOUT the wx wasm
|
||||||
|
port. `wxWasmBeginWait`/`wxWasmYieldUntil` are declared WEAK there with a null guard
|
||||||
|
returning "request unavailable" — correct, since a converter diet can never have a
|
||||||
|
`/mnt/pcbjam` provider either. (Same class as the nanosleep lesson above.)
|
||||||
|
|
||||||
|
**Phase E remaining before it is DONE done:** the §5 assertion ("no `handleSleep`
|
||||||
|
park happens on any fiber stack — assert it") is not built, per-bridge deep-park
|
||||||
|
high-water beacons for buffer sizing are not collected, and the standalone/web smoke
|
||||||
|
with the production provider is still owed. The natural next measurement is a D-on
|
||||||
|
probe: with every bridge a token wait, the `current() != 0` inline fallback window
|
||||||
|
that D-on tolerated should now be structurally empty.
|
||||||
|
|
||||||
1. **pthreads.** Doc 21 §2 settled that every Asyncify park is main-thread and the lib
|
1. **pthreads.** Doc 21 §2 settled that every Asyncify park is main-thread and the lib
|
||||||
bridge's worker path is a blocking proxy. Phase A must re-check that libcontext is never
|
bridge's worker path is a blocking proxy. Phase A must re-check that libcontext is never
|
||||||
driven from a worker before assuming the scheduler is main-thread-only.
|
driven from a worker before assuming the scheduler is main-thread-only.
|
||||||
|
|
|
||||||
2
kicad
2
kicad
|
|
@ -1 +1 @@
|
||||||
Subproject commit ab1ac9b940596a97820eab3b8403f443db1e111a
|
Subproject commit 72b754b2c5552d7596ad5e5a31721eddb048f53e
|
||||||
|
|
@ -53,16 +53,28 @@ extern "C" void Pcbjam_SetExportJobJson( const char* aJson )
|
||||||
s_nextJobJson = aJson ? aJson : "";
|
s_nextJobJson = aJson ? aJson : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suspends pcbnew (Asyncify; the __asyncjs__* import is auto-covered by
|
// Phase E shape (docs/features/async/22 §5, K4): waits for the worker export
|
||||||
// scripts/common/asyncify-imports.txt) while the worker exports. JS returns a
|
// via a token wait (context park when the frame stands on a scheduler context)
|
||||||
// malloc'd JSON string: { ok, report } — the download already happened there.
|
// instead of Asyncify-parking the stack in place. The wait result is a
|
||||||
EM_ASYNC_JS( char*, js_occExportRequest,
|
// malloc'd JSON string: { ok, report } — the download already happened in JS.
|
||||||
( const char* aBoardPath, const char* aJobJson, const char* aFileName ),
|
// Every resolution defers to at least a microtask (the early-resolve
|
||||||
|
// contract, doc 22 §10 Phase E retry entry).
|
||||||
|
EM_JS( void, js_occExportStart,
|
||||||
|
( int aToken, const char* aBoardPath, const char* aJobJson, const char* aFileName ),
|
||||||
{
|
{
|
||||||
const boardPath = UTF8ToString( aBoardPath );
|
const boardPath = UTF8ToString( aBoardPath );
|
||||||
const jobJson = UTF8ToString( aJobJson );
|
const jobJson = UTF8ToString( aJobJson );
|
||||||
const fileName = UTF8ToString( aFileName );
|
const fileName = UTF8ToString( aFileName );
|
||||||
let res;
|
|
||||||
|
const finish = ( res ) => {
|
||||||
|
const s = JSON.stringify( res || { ok: false, report: 'occ_service: no response' } );
|
||||||
|
const n = lengthBytesUTF8( s ) + 1;
|
||||||
|
const p = _malloc( n );
|
||||||
|
stringToUTF8( s, p, n );
|
||||||
|
globalThis.__wxScheduler.resolveWait( aToken, p );
|
||||||
|
};
|
||||||
|
|
||||||
|
let req;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -70,27 +82,30 @@ EM_ASYNC_JS( char*, js_occExportRequest,
|
||||||
|
|
||||||
if( !hook || typeof hook.request !== 'function' )
|
if( !hook || typeof hook.request !== 'function' )
|
||||||
{
|
{
|
||||||
res = { ok: false, report: 'occ_service provider not installed' };
|
req = Promise.resolve( { ok: false, report: 'occ_service provider not installed' } );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const board = FS.readFile( boardPath ); // Uint8Array copy — transferable
|
const board = FS.readFile( boardPath ); // Uint8Array copy — transferable
|
||||||
res = await hook.request( { kind: 'export', board, jobJson, fileName } );
|
req = Promise.resolve( hook.request( { kind: 'export', board, jobJson, fileName } ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch( e )
|
catch( e )
|
||||||
{
|
{
|
||||||
console.error( '[pcbjam-occ] export request failed:', e );
|
console.error( '[pcbjam-occ] export request failed:', e );
|
||||||
res = { ok: false, report: 'occ_service request failed: ' + e };
|
req = Promise.resolve( { ok: false, report: 'occ_service request failed: ' + e } );
|
||||||
}
|
}
|
||||||
|
|
||||||
const s = JSON.stringify( res || { ok: false, report: 'occ_service: no response' } );
|
req.then( finish ).catch( ( e ) => {
|
||||||
const n = lengthBytesUTF8( s ) + 1;
|
console.error( '[pcbjam-occ] export request failed:', e );
|
||||||
const p = _malloc( n );
|
finish( { ok: false, report: 'occ_service request failed: ' + e } );
|
||||||
stringToUTF8( s, p, n );
|
} );
|
||||||
return p;
|
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
// Token waits live in the wx wasm port (evtloop.cpp).
|
||||||
|
extern "C" int wxWasmBeginWait( const char* aKind );
|
||||||
|
extern "C" int wxWasmYieldUntil( int aToken );
|
||||||
|
|
||||||
|
|
||||||
EXPORTER_STEP::EXPORTER_STEP( BOARD* aBoard, const EXPORTER_STEP_PARAMS& aParams,
|
EXPORTER_STEP::EXPORTER_STEP( BOARD* aBoard, const EXPORTER_STEP_PARAMS& aParams,
|
||||||
REPORTER* aReporter ) :
|
REPORTER* aReporter ) :
|
||||||
|
|
@ -152,8 +167,11 @@ bool EXPORTER_STEP::Export()
|
||||||
|
|
||||||
const wxString downloadName = wxFileName( m_outputFile ).GetFullName();
|
const wxString downloadName = wxFileName( m_outputFile ).GetFullName();
|
||||||
|
|
||||||
char* response = js_occExportRequest( TMP_BOARD, jobJson.c_str(),
|
const int token = wxWasmBeginWait( "occ" );
|
||||||
downloadName.utf8_string().c_str() );
|
js_occExportStart( token, TMP_BOARD, jobJson.c_str(), downloadName.utf8_string().c_str() );
|
||||||
|
|
||||||
|
// The malloc'd JSON pointer rides the wait as an int32.
|
||||||
|
char* response = (char*) (uintptr_t) (uint32_t) wxWasmYieldUntil( token );
|
||||||
|
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,13 +58,23 @@ bool acceptAnyCacheTag( const char*, void* )
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Suspends pcbnew while the worker parses + tessellates the model. JS returns
|
// Phase E shape (docs/features/async/22 §5, K5): waits for the worker
|
||||||
// a malloc'd path string: the scenegraph-cache file it wrote into this
|
// parse+tessellate via a token wait instead of Asyncify-parking in place. The
|
||||||
// module's MEMFS ("" on failure).
|
// wait result is a malloc'd path string: the scenegraph-cache file written
|
||||||
EM_ASYNC_JS( char*, js_occLoadModelRequest, ( const char* aModelPath ),
|
// into this module's MEMFS ("" on failure). Every resolution defers to at
|
||||||
|
// least a microtask (the early-resolve contract, doc 22 §10 Phase E retry).
|
||||||
|
EM_JS( void, js_occLoadModelStart, ( int aToken, const char* aModelPath ),
|
||||||
{
|
{
|
||||||
const modelPath = UTF8ToString( aModelPath );
|
const modelPath = UTF8ToString( aModelPath );
|
||||||
let cachePath = '';
|
|
||||||
|
const finish = ( cachePath ) => {
|
||||||
|
const n = lengthBytesUTF8( cachePath ) + 1;
|
||||||
|
const p = _malloc( n );
|
||||||
|
stringToUTF8( cachePath, p, n );
|
||||||
|
globalThis.__wxScheduler.resolveWait( aToken, p );
|
||||||
|
};
|
||||||
|
|
||||||
|
let req;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -73,13 +83,24 @@ EM_ASYNC_JS( char*, js_occLoadModelRequest, ( const char* aModelPath ),
|
||||||
if( !hook || typeof hook.request !== 'function' )
|
if( !hook || typeof hook.request !== 'function' )
|
||||||
{
|
{
|
||||||
console.error( '[pcbjam-occ] loadModel: occ_service provider not installed' );
|
console.error( '[pcbjam-occ] loadModel: occ_service provider not installed' );
|
||||||
|
req = Promise.resolve( null );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const bytes = FS.readFile( modelPath ); // Uint8Array copy — transferable
|
const bytes = FS.readFile( modelPath ); // Uint8Array copy — transferable
|
||||||
const dot = modelPath.lastIndexOf( '.' );
|
const dot = modelPath.lastIndexOf( '.' );
|
||||||
const ext = dot >= 0 ? modelPath.slice( dot + 1 ) : 'step';
|
const ext = dot >= 0 ? modelPath.slice( dot + 1 ) : 'step';
|
||||||
const res = await hook.request( { kind: 'loadModel', bytes, ext } );
|
req = Promise.resolve( hook.request( { kind: 'loadModel', bytes, ext } ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch( e )
|
||||||
|
{
|
||||||
|
console.error( '[pcbjam-occ] loadModel request failed:', e );
|
||||||
|
req = Promise.resolve( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
req.then( ( res ) => {
|
||||||
|
let cachePath = '';
|
||||||
|
|
||||||
if( res && res.ok && res.bytes && res.bytes.length )
|
if( res && res.ok && res.bytes && res.bytes.length )
|
||||||
{
|
{
|
||||||
|
|
@ -90,20 +111,18 @@ EM_ASYNC_JS( char*, js_occLoadModelRequest, ( const char* aModelPath ),
|
||||||
{
|
{
|
||||||
console.error( '[pcbjam-occ] loadModel failed:', res.report );
|
console.error( '[pcbjam-occ] loadModel failed:', res.report );
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( e )
|
|
||||||
{
|
|
||||||
console.error( '[pcbjam-occ] loadModel request failed:', e );
|
|
||||||
cachePath = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const n = lengthBytesUTF8( cachePath ) + 1;
|
finish( cachePath );
|
||||||
const p = _malloc( n );
|
} ).catch( ( e ) => {
|
||||||
stringToUTF8( cachePath, p, n );
|
console.error( '[pcbjam-occ] loadModel request failed:', e );
|
||||||
return p;
|
finish( '' );
|
||||||
|
} );
|
||||||
} )
|
} )
|
||||||
|
|
||||||
|
// Token waits live in the wx wasm port (evtloop.cpp).
|
||||||
|
extern "C" int wxWasmBeginWait( const char* aKind );
|
||||||
|
extern "C" int wxWasmYieldUntil( int aToken );
|
||||||
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
@ -205,7 +224,11 @@ SCENEGRAPH* oce3d_Load( char const* aFileName )
|
||||||
if( !aFileName )
|
if( !aFileName )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
char* cachePath = js_occLoadModelRequest( aFileName );
|
const int token = wxWasmBeginWait( "occ" );
|
||||||
|
js_occLoadModelStart( token, aFileName );
|
||||||
|
|
||||||
|
// The malloc'd path pointer rides the wait as an int32.
|
||||||
|
char* cachePath = (char*) (uintptr_t) (uint32_t) wxWasmYieldUntil( token );
|
||||||
|
|
||||||
if( !cachePath || !*cachePath )
|
if( !cachePath || !*cachePath )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -56,47 +56,61 @@ using nlohmann::json;
|
||||||
|
|
||||||
// Generic request: JSON in, JSON out (malloc'd; caller frees). Vector data
|
// Generic request: JSON in, JSON out (malloc'd; caller frees). Vector data
|
||||||
// never travels this path — see js_ngspice_get_vec.
|
// never travels this path — see js_ngspice_get_vec.
|
||||||
|
//
|
||||||
|
// Phase E shape (docs/features/async/22 §5, K6): token wait instead of an
|
||||||
|
// in-place Asyncify park; resolution ALWAYS deferred to at least a microtask
|
||||||
|
// (the early-resolve contract, doc 22 §10 Phase E retry entry).
|
||||||
// clang-format off
|
// clang-format off
|
||||||
EM_ASYNC_JS( char*, js_ngspice_request, ( const char* aReqJson ), {
|
EM_JS( void, js_ngspice_request_start, ( int aToken, const char* aReqJson ), {
|
||||||
let res;
|
const finish = ( res ) => {
|
||||||
try {
|
|
||||||
const svc = globalThis.ngspiceService;
|
|
||||||
if( !svc )
|
|
||||||
res = { error: 'ngspiceService provider not installed' };
|
|
||||||
else
|
|
||||||
res = await svc.request( JSON.parse( UTF8ToString( aReqJson ) ) );
|
|
||||||
} catch( e ) {
|
|
||||||
res = { error: String( e ) };
|
|
||||||
}
|
|
||||||
const s = JSON.stringify( res ?? {} );
|
const s = JSON.stringify( res ?? {} );
|
||||||
const n = lengthBytesUTF8( s ) + 1;
|
const n = lengthBytesUTF8( s ) + 1;
|
||||||
const p = _malloc( n );
|
const p = _malloc( n );
|
||||||
stringToUTF8( s, p, n );
|
stringToUTF8( s, p, n );
|
||||||
return p;
|
globalThis.__wxScheduler.resolveWait( aToken, p );
|
||||||
|
};
|
||||||
|
let req;
|
||||||
|
try {
|
||||||
|
const svc = globalThis.ngspiceService;
|
||||||
|
if( !svc )
|
||||||
|
req = Promise.resolve( { error: 'ngspiceService provider not installed' } );
|
||||||
|
else
|
||||||
|
req = Promise.resolve( svc.request( JSON.parse( UTF8ToString( aReqJson ) ) ) );
|
||||||
|
} catch( e ) {
|
||||||
|
req = Promise.resolve( { error: String( e ) } );
|
||||||
|
}
|
||||||
|
req.then( finish ).catch( ( e ) => finish( { error: String( e ) } ) );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Vector fetch: fills editor-heap buffers directly (no JSON for MB arrays).
|
// Vector fetch: fills editor-heap buffers directly (no JSON for MB arrays).
|
||||||
// aMeta: int[4] = { found, vtype, flags, length }; aReal/aComp receive
|
// aMeta: int[4] = { found, vtype, flags, length }; aReal/aComp receive
|
||||||
// malloc'd double buffers (comp interleaved re,im — the ngcomplex_t layout);
|
// malloc'd double buffers (comp interleaved re,im — the ngcomplex_t layout);
|
||||||
// aVName receives a malloc'd name string. Returns non-zero on transport error.
|
// aVName receives a malloc'd name string. The wait result is non-zero on
|
||||||
EM_ASYNC_JS( int, js_ngspice_get_vec,
|
// transport error. All output writes happen in the resolve callback, BEFORE
|
||||||
( const char* aName, int* aMeta, double** aReal, double** aComp, char** aVName ), {
|
// resolveWait — the parked caller reads them only after it resumes, the same
|
||||||
let res;
|
// ordering the in-place park had. Phase E shape, resolution always deferred.
|
||||||
|
EM_JS( void, js_ngspice_get_vec_start,
|
||||||
|
( int aToken, const char* aName, int* aMeta, double** aReal, double** aComp,
|
||||||
|
char** aVName ), {
|
||||||
|
const finish = ( status ) => globalThis.__wxScheduler.resolveWait( aToken, status );
|
||||||
|
let req;
|
||||||
try {
|
try {
|
||||||
const svc = globalThis.ngspiceService;
|
const svc = globalThis.ngspiceService;
|
||||||
res = svc ? await svc.request( { kind: 'get_vec_info', name: UTF8ToString( aName ) } )
|
req = svc ? Promise.resolve( svc.request( { kind: 'get_vec_info',
|
||||||
: { error: 'ngspiceService provider not installed' };
|
name: UTF8ToString( aName ) } ) )
|
||||||
|
: Promise.resolve( { error: 'ngspiceService provider not installed' } );
|
||||||
} catch( e ) {
|
} catch( e ) {
|
||||||
res = { error: String( e ) };
|
req = Promise.resolve( { error: String( e ) } );
|
||||||
}
|
}
|
||||||
|
req.catch( ( e ) => ( { error: String( e ) } ) ).then( ( res ) => {
|
||||||
HEAP32[aMeta >> 2] = 0;
|
HEAP32[aMeta >> 2] = 0;
|
||||||
HEAPU32[aReal >> 2] = 0;
|
HEAPU32[aReal >> 2] = 0;
|
||||||
HEAPU32[aComp >> 2] = 0;
|
HEAPU32[aComp >> 2] = 0;
|
||||||
HEAPU32[aVName >> 2] = 0;
|
HEAPU32[aVName >> 2] = 0;
|
||||||
if( !res || res.error )
|
if( !res || res.error )
|
||||||
return 1;
|
return finish( 1 );
|
||||||
if( !res.found )
|
if( !res.found )
|
||||||
return 0;
|
return finish( 0 );
|
||||||
HEAP32[( aMeta >> 2 ) + 1] = res.vtype | 0;
|
HEAP32[( aMeta >> 2 ) + 1] = res.vtype | 0;
|
||||||
HEAP32[( aMeta >> 2 ) + 2] = res.flags | 0;
|
HEAP32[( aMeta >> 2 ) + 2] = res.flags | 0;
|
||||||
HEAP32[( aMeta >> 2 ) + 3] = res.length | 0;
|
HEAP32[( aMeta >> 2 ) + 3] = res.length | 0;
|
||||||
|
|
@ -116,8 +130,13 @@ EM_ASYNC_JS( int, js_ngspice_get_vec,
|
||||||
stringToUTF8( s, vp, n );
|
stringToUTF8( s, vp, n );
|
||||||
HEAPU32[aVName >> 2] = vp;
|
HEAPU32[aVName >> 2] = vp;
|
||||||
HEAP32[aMeta >> 2] = 1;
|
HEAP32[aMeta >> 2] = 1;
|
||||||
return 0;
|
finish( 0 );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Token waits live in the wx wasm port (evtloop.cpp).
|
||||||
|
extern "C" int wxWasmBeginWait( const char* aKind );
|
||||||
|
extern "C" int wxWasmYieldUntil( int aToken );
|
||||||
|
|
||||||
// Event dispatcher: provider `{ evt }` frames -> KiCad's registered callbacks
|
// Event dispatcher: provider `{ evt }` frames -> KiCad's registered callbacks
|
||||||
// via the exported pcbjam_ngspice_event (fresh wasm entries; see header
|
// via the exported pcbjam_ngspice_event (fresh wasm entries; see header
|
||||||
|
|
@ -166,7 +185,11 @@ std::atomic<bool> s_bgRunning{ false };
|
||||||
|
|
||||||
json rpc( const json& aReq )
|
json rpc( const json& aReq )
|
||||||
{
|
{
|
||||||
char* raw = js_ngspice_request( aReq.dump().c_str() );
|
const int token = wxWasmBeginWait( "ngspice" );
|
||||||
|
js_ngspice_request_start( token, aReq.dump().c_str() );
|
||||||
|
|
||||||
|
// The malloc'd JSON pointer rides the wait as an int32.
|
||||||
|
char* raw = (char*) (uintptr_t) (uint32_t) wxWasmYieldUntil( token );
|
||||||
json res = json::parse( raw ? raw : "{}", nullptr, /* allow_exceptions */ false );
|
json res = json::parse( raw ? raw : "{}", nullptr, /* allow_exceptions */ false );
|
||||||
std::free( raw );
|
std::free( raw );
|
||||||
|
|
||||||
|
|
@ -408,7 +431,10 @@ pvector_info pcbjam_ngGet_Vec_Info( char* aVecName )
|
||||||
double* comp = nullptr;
|
double* comp = nullptr;
|
||||||
char* vname = nullptr;
|
char* vname = nullptr;
|
||||||
|
|
||||||
if( js_ngspice_get_vec( aVecName ? aVecName : "", meta, &real, &comp, &vname ) != 0 )
|
const int token = wxWasmBeginWait( "ngspice" );
|
||||||
|
js_ngspice_get_vec_start( token, aVecName ? aVecName : "", meta, &real, &comp, &vname );
|
||||||
|
|
||||||
|
if( wxWasmYieldUntil( token ) != 0 )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if( !meta[0] )
|
if( !meta[0] )
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue