Skip to content

Run a simulation in its own thread#532

Merged
agarny merged 26 commits into
opencor:mainfrom
agarny:issue63
Jul 23, 2026
Merged

Run a simulation in its own thread#532
agarny merged 26 commits into
opencor:mainfrom
agarny:issue63

Conversation

@agarny

@agarny agarny commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #63.

Copilot AI review requested due to automatic review settings July 23, 2026 06:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enables running libOpenCOR simulations asynchronously (in a background thread) so the UI can remain responsive and support progressive/interactive simulation updates, aligning with Issue #63’s goal of “gradual” simulation execution similar to legacy OpenCOR.

Changes:

  • Replaced the synchronous sedInstanceRun API with a threaded run lifecycle: status/progress/start/wait/pause/resume/stop (C++ N-API + TS wrapper + preload bridge).
  • Updated the Simulation Experiment view to support run/pause/resume/stop controls and a progress bar, including polling to keep UI responsive.
  • Adjusted web initialization and dev-server configuration to support Emscripten pthread requirements (same-origin loading + COOP/COEP in dev), plus minor plotting improvements (optional axis ranges).

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/renderer/vite.config.ts Adds COOP/COEP headers for dev server and proxies /libopencor-wasm to serve WASM assets same-origin.
src/renderer/src/libopencor/src/sed.h Updates N-API surface to threaded run lifecycle functions.
src/renderer/src/libopencor/src/sed.cpp Implements new N-API exports for status/progress/start/wait/pause/resume/stop.
src/renderer/src/libopencor/src/main.cpp Exposes the new SedInstance run lifecycle functions to JS.
src/renderer/src/libopencor/locSedApi.ts Adds ESedInstanceStatus and SedInstance lifecycle methods for both C++ and WASM backends.
src/renderer/src/libopencor/locApi.ts Extends the C++ bridge interface with the new SedInstance lifecycle functions.
src/renderer/src/components/widgets/GraphPanelWidget.vue Adds optional axis range constraints into Plotly layout.
src/renderer/src/components/views/SimulationExperimentView.vue Implements run/pause/resume/stop UI, progress polling, and interactive cancellation/restart logic.
src/renderer/src/components/propertyEditors/SimulationPropertyEditor.vue Adds a disabled prop passthrough to prevent edits while running.
src/renderer/src/components/propertyEditors/PropertyEditor.vue Disables DataTable cell editing when disabled is true.
src/renderer/src/common/initialisation.ts Switches WASM loading to a same-origin proxied path to support pthread workers.
src/renderer/src/common/constants.ts Adds VERY_SHORT_DELAY for tight polling intervals.
src/renderer/package.json Bumps app + libOpenCOR types versions and dev deps.
src/renderer/index.html Updates CSP to allow same-origin workers (worker-src 'self').
src/renderer/bun.lock Lockfile updates for renderer package.
src/preload/index.ts Bridges new SedInstance lifecycle APIs into window.locApi.
src/extern/corsProxy.js Refactors worker CORS proxy with helpers; adjusts error handling behavior.
package.json Bumps root app version and dev deps.
bun.lock Lockfile updates for root package.
Comments suppressed due to low confidence (2)

src/renderer/src/components/views/SimulationExperimentView.vue:1602

  • The interactive run path starts a background run and waits for the status to return to IDLE, but never calls waitForRun(). If waitForRun() is required to join/finalise the run (and surface timing/errors deterministically), this can leave the worker/thread unjoined and accumulate state across updates/cancellations.
  interactiveInstance.startRun();

  await waitWhileRunning(interactiveInstance);

  // Check if we have been superseded by a newer call while the simulation was running.

  if (currentSimulationGeneration !== interactiveSimulationGeneration) {
    return;

src/renderer/src/components/views/SimulationExperimentView.vue:535

  • onStop() forces standardSimulationStatus to IDLE immediately after calling stopRun(). If stopping is asynchronous, this can temporarily re-enable settings/UI while the simulation is still winding down. Let the polling loop update the status (or read it back from the instance) instead of forcing IDLE.
const onStop = (): void => {
  standardRunAborted = true;

  standardInstance.stopRun();

  standardSimulationStatus.value = locSedApi.ESedInstanceStatus.IDLE;
};

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/renderer/src/components/views/SimulationExperimentView.vue
Comment thread src/renderer/src/components/views/SimulationExperimentView.vue Outdated
Comment thread src/renderer/vite.config.ts
Comment thread src/extern/corsProxy.js
@agarny
agarny force-pushed the issue63 branch 4 times, most recently from 4a1185a to 9e0417e Compare July 23, 2026 11:15
agarny added 20 commits July 24, 2026 00:02
We don't need to yield to the UI thread.
This model takes about half a second to run, which is good for testing UI interaction when running a simulation. The plan is indeed to cancel a simulation and starting a new one if simulation inputs change.
…ore the underlying C++ thread has fully stopped.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 22 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (4)

src/renderer/src/components/views/SimulationExperimentView.vue:1630

  • SedInstance.startRun() returns a boolean, but it’s ignored here. If a background run fails to start, continuing to await waitWhileRunning() can mask the failure and make debugging harder. Consider handling the false case and surfacing issues early.
  interactiveInstance.startRun();

  await waitWhileRunning(interactiveInstance).promise;

src/renderer/src/components/views/SimulationExperimentView.vue:377

  • After introducing cancelled, onStatusChange should not be invoked once cancellation has happened (e.g., on component unmount), otherwise it can still write to stale refs.
      if (status !== lastStatus) {
        lastStatus = status;

        onStatusChange?.(status);
      }

src/renderer/src/components/views/SimulationExperimentView.vue:388

  • After introducing cancelled, suppress progress updates when cancelled so waitWhileRunning() doesn’t keep mutating state after unmount.
      switch (status) {
        case locSedApi.ESedInstanceStatus.RUNNING:
          onProgress?.(100 * instance.progress());

          setTimeout(poll, VERY_SHORT_DELAY);

          break;

src/renderer/src/components/views/SimulationExperimentView.vue:402

  • The IDLE branch still schedules a progress reset timer and calls onProgress even after cancellation. Gate this block on !cancelled so unmount cancellation actually prevents late progress writes.
        default: // locSedApi.ESedInstanceStatus.IDLE:
          if (onProgress && !standardRunAborted) {
            onProgress(100);

            // Reset the progress bar after a short delay.

            progressResetTimer = setTimeout(() => {
              onProgress?.(0);
            }, MEDIUM_DELAY);
          }

Comment thread src/renderer/src/components/widgets/GraphPanelWidget.vue
Comment thread src/renderer/src/components/views/SimulationExperimentView.vue Outdated
Comment thread src/renderer/src/common/initialisation.ts Outdated
Comment thread src/renderer/src/components/views/SimulationExperimentView.vue
agarny added 5 commits July 24, 2026 00:19
Indeed, this keeps existing axis config intact while making the update safer for cases where axis objects may be undefined and more reliable for reactive layout updates.
…to start.

Indeed, if a simulation fails to start, the standard path now refreshes the displayed status and exits early, while the interactive path returns immediately instead of waiting on a run that never started.
Replace the hardcoded libOpenCOR version define with a base-URL define and use it when loading the WASM bundle. This keeps the same-origin dev proxy behavior while allowing production web builds to point libOpenCOR assets at a custom host or path, and drops the unused define from the library Vite config.
This allows to exist  the poll loop early and to resolve immediately when cancelled. Also guard delayed progress reset callbacks so they no longer emit `onProgress(0)` after cancellation.
Indeed, this avoids clearing `standardData` (axis titles and traces) at run start so existing plotting state is preserved.
@agarny
agarny merged commit 7983b31 into opencor:main Jul 23, 2026
9 checks passed
@agarny
agarny deleted the issue63 branch July 23, 2026 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run a simulation in its own thread

2 participants