Cancel running workflow - #14225
Conversation
There was a problem hiding this comment.
Pull request overview
Makes workflow cancellation non-blocking and adds cooperative cancellation support for internal jobs and exports.
Changes:
- Removes blocking waits from
WorkflowRunner.cancel(). - Adds cancellation polling and regression tests.
- Updates workflow UI layout, labels, and documentation.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/ert/workflow_runner.py |
Makes cancellation non-blocking and documents behavior. |
src/ert/config/ert_script.py |
Documents cooperative cancellation. |
src/ert/plugins/hook_implementations/workflows/csv_export.py |
Stops processing ensembles after cancellation. |
src/ert/plugins/hook_implementations/workflows/export_rft.py |
Stops exporting realizations after cancellation. |
src/ert/gui/ertwidgets/closabledialog.py |
Supports left-aligned dialog buttons. |
src/ert/gui/tools/workflows/workflows_tool.py |
Moves the start button into the dialog footer. |
src/ert/gui/tools/workflows/workflow_dialog.py |
Renames the running-dialog action to Cancel. |
src/ert/gui/tools/workflows/run_workflow_widget.py |
Cleans up workflow controls and cancellation UI. |
tests/ert/unit_tests/workflow_runner/workflow_common.py |
Adds an uncooperative internal-job fixture. |
tests/ert/unit_tests/workflow_runner/test_workflow_runner.py |
Tests non-blocking cancellation. |
tests/ert/unit_tests/resources/test_csv_export.py |
Tests CSV export cancellation. |
tests/ert/unit_tests/plugins/test_export_rft.py |
Tests RFT export cancellation. |
docs/ert/reference/workflows/workflows.rst |
Documents cooperative cancellation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Screenshots differ from baselines. A baseline update PR has been prepared: equinor/ert-testdata#88 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## store-workflow-output #14225 +/- ##
=========================================================
+ Coverage 91.79% 91.81% +0.02%
=========================================================
Files 486 486
Lines 34902 34908 +6
=========================================================
+ Hits 32038 32051 +13
+ Misses 2864 2857 -7
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
You need to rebase |
774c756 to
12fdc57
Compare
12fdc57 to
9282df1
Compare
|
Screenshot tests now pass. The baseline update PR equinor/ert-testdata#88 has been closed. |
9282df1 to
4486d36
Compare
4486d36 to
36b5b1e
Compare
36b5b1e to
4372356
Compare
4372356 to
7186524
Compare
Cancelling an internal ErtScript workflow job is cooperative: cancel() only sets a flag, nothing forcibly interrupts a running run(). A job that never checks isCancelled() runs to completion regardless of the user's request, and none of the jobs ERT ships did. Document the contract on ErtScript.isCancelled() and in the workflow reference, and adopt it in the two bundled jobs that loop over realizations or ensembles: CSVExportJob and ExportRFTJob. Both now stop before doing further work once cancellation is requested, without losing what they already produced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
WorkflowRunner.cancel() used to call wait(), which blocks until the currently running job actually stops. For an internal ErtScript that does not poll isCancelled(), this can block for as long as the job keeps running - and since cancel() is called directly on the Qt main thread (GUI) or the CLI's main thread, this freezes the whole application until the job finishes on its own. cancel() is now purely a 'signal, don't block' call: it still asks the current job to stop (which forcibly terminates external jobs), but no longer waits for that to happen. Callers that need to block until the workflow has actually stopped can still call wait() explicitly. Add a regression test using an internal job that deliberately never checks isCancelled(), proving cancel() returns promptly regardless.
Make 'Start workflow' a plain button matching 'Close' in size and style (no icon, no oversized tool button), placed in the bottom-left corner so it lines up with 'Close' in the bottom-right - instead of sitting as an oversized row inside the form above. Also rename the 'Running workflow' progress dialog's single button from 'Close' to 'Cancel', since clicking it has only ever cancelled the running workflow, never just closed the dialog.
The user already confirmed the cancellation via the 'Confirm cancel' dialog, and the 'Running workflow' progress dialog simply closes. A second 'was killed successfully!' popup afterwards is redundant.
WorkflowRunner.cancel() could race with run_blocking() publishing the next WorkflowJobRunner as __current_job, causing cancel() to target a stale job. Even after publication, WorkflowJobRunner.cancel() was a no-op until run() had assigned its ErtScript, so a job could start without ever receiving the cancellation. Add a lock around checking/publishing __current_job in WorkflowRunner so cancel() always reads the job that is actually about to run. Add a pending-cancel flag to WorkflowJobRunner, applied to the script as soon as it is created, so a cancel() arriving before the script exists is no longer lost. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7186524 to
f80fa2e
Compare
Issue
Resolves #14090
Approach
Update jobs to follow proper cancelation logic.
Stop cancel() from blocking UI/CLI thread.
Minor UI cleanup in workflow dialog.
(Screenshot of new behavior in GUI if applicable)

git rebase -i main --exec 'just rapid-tests')When applicable
merge screenshot-PR in ert-testdata before merging this PR.