Skip to content

Rename TaskFuture.abort to request_stop, execute subprocess tasks with pebble, add abort and kill - #501

Open
woutdenolf wants to merge 10 commits into
mainfrom
use_task_request_stop
Open

woutdenolf wants to merge 10 commits into
mainfrom
use_task_request_stop

Conversation

@woutdenolf

@woutdenolf woutdenolf commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

PR summary

  • Add abort (interrupts the task like Ctrl-C, with cleanup) and kill (terminates the worker process). Both work only with a process pool, which now uses pebble. They come on top of cancel and request_stop.
  • Calling cancel, request_stop, abort or kill on a task that is still queued prevents it from starting.
    • The call returns immediately. The future completes (and raises) once a worker dequeues the task, except:
      • cancel on the thread and sync backends also cancels the future, which completes right away.
      • kill also cancels the pebble future, which completes right away.
    • cancel, request_stop and abort do not cancel the pebble future: cancelling it kills the worker if one just picked up the task, which would turn them into a kill.
  • Process pool: replace the inter-process communication with two queues, controller→runner and runner→controller.

Try-out cancel vs. stop vs. abort vs. kill with

python scripts/ewoks_executor_demo.py --task-duration 5
Click Submit x5 (Process parallel) followed by cancel, stop, abort or kill
# Submit x5 (Process parallel) + cancel
[   4.458s] [P PARALLEL] Submitted -> 140536643910208
[   4.469s] [P PARALLEL] Submitted -> 140536643993728
[   4.481s] [P PARALLEL] Submitted -> 140536643995120
[   4.492s] [P PARALLEL] Submitted -> 140536643996608
[   4.502s] [P PARALLEL] Submitted -> 140536643998240
[   4.502s] [P PARALLEL] ▶ Started -> 140536643910208
[   4.503s] [P PARALLEL] ▶ Started -> 140536643993728
[   4.503s] [P PARALLEL] ▶ Started -> 140536643995120
[   4.503s] [P PARALLEL] ▶ Started -> 140536643996608
[   5.680s] Cancel (queued only): accepted by 1/5 tasks
[   9.486s] [P PARALLEL] ✅ 140536643910208 -> {'result': 2}
[   9.536s] [P PARALLEL] ■ Finished -> 140536643910208
[   9.539s] [P PARALLEL] ✅ 140536643993728 -> {'result': 4}
[   9.540s] [P PARALLEL] ■ Finished -> 140536643993728
[   9.541s] [P PARALLEL] ✅ 140536643995120 -> {'result': 6}
[   9.541s] [P PARALLEL] ■ Finished -> 140536643995120
[   9.543s] [P PARALLEL] ✅ 140536643996608 -> {'result': 8}
[   9.544s] [P PARALLEL] ■ Finished -> 140536643996608
[   9.544s] [P PARALLEL] 🚫 Cancelled -> 140536643998240
[   9.646s] [P PARALLEL] ■ Finished -> 140536643998240

# Submit x5 (Process parallel) + stop
[  12.566s] [P PARALLEL] Submitted -> 140536643997520
[  12.580s] [P PARALLEL] Submitted -> 140536644000592
[  12.597s] [P PARALLEL] Submitted -> 140536644002080
[  12.611s] [P PARALLEL] Submitted -> 140536644003568
[  12.622s] [P PARALLEL] Submitted -> 140536644005008
[  12.623s] [P PARALLEL] ▶ Started -> 140536643997520
[  12.625s] [P PARALLEL] ▶ Started -> 140536644000592
[  12.626s] [P PARALLEL] ▶ Started -> 140536644002080
[  12.626s] [P PARALLEL] ▶ Started -> 140536644003568
[  14.051s] Request stop: accepted by 5/5 tasks
[  14.078s] [P PARALLEL] ⏹ Stop requested -> 140536644002080
[  14.105s] [P PARALLEL] ❌ 140536644002080 -> Execution failed for ewoks task (id: None, task: '__main__.SumTask'): Stopped on request
[  14.106s] [P PARALLEL] ■ Finished -> 140536644002080
[  14.108s] [P PARALLEL] ⏹ Stop requested -> 140536644003568
[  14.109s] [P PARALLEL] ❌ 140536644003568 -> Execution failed for ewoks task (id: None, task: '__main__.SumTask'): Stopped on request
[  14.109s] [P PARALLEL] ■ Finished -> 140536644003568
[  14.110s] [P PARALLEL] ⏹ Stop requested -> 140536643997520
[  14.110s] [P PARALLEL] ❌ 140536643997520 -> Execution failed for ewoks task (id: None, task: '__main__.SumTask'): Stopped on request
[  14.110s] [P PARALLEL] ■ Finished -> 140536643997520
[  14.111s] [P PARALLEL] ⏹ Stop requested -> 140536644005008
[  14.111s] [P PARALLEL] ❌ 140536644005008 -> Task SumTask was stopped before it started
[  14.111s] [P PARALLEL] ■ Finished -> 140536644005008
[  14.114s] [P PARALLEL] ⏹ Stop requested -> 140536644000592
[  14.114s] [P PARALLEL] ❌ 140536644000592 -> Execution failed for ewoks task (id: None, task: '__main__.SumTask'): Stopped on request
[  14.114s] [P PARALLEL] ■ Finished -> 140536644000592

# Submit x5 (Process parallel) + abort
[  18.367s] [P PARALLEL] Submitted -> 140536644177840
[  18.379s] [P PARALLEL] Submitted -> 140536644179616
[  18.401s] [P PARALLEL] Submitted -> 140536644181152
[  18.411s] [P PARALLEL] Submitted -> 140536644182736
[  18.423s] [P PARALLEL] Submitted -> 140536644184320
[  18.424s] [P PARALLEL] ▶ Started -> 140536644177840
[  18.425s] [P PARALLEL] ▶ Started -> 140536644179616
[  18.425s] [P PARALLEL] ▶ Started -> 140536644181152
[  18.425s] [P PARALLEL] ▶ Started -> 140536644182736
[  20.026s] Abort (process only): accepted by 5/5 tasks
[  20.030s] [P PARALLEL] ⛔ Aborted -> 140536644179616
[  20.031s] [P PARALLEL] ❌ 140536644179616 -> Task SumTask was aborted
[  20.032s] [P PARALLEL] ■ Finished -> 140536644179616
[  20.041s] [P PARALLEL] ⛔ Aborted -> 140536644182736
[  20.042s] [P PARALLEL] ❌ 140536644182736 -> Task SumTask was aborted
[  20.044s] [P PARALLEL] ■ Finished -> 140536644182736
[  20.045s] [P PARALLEL] ⛔ Aborted -> 140536644177840
[  20.045s] [P PARALLEL] ❌ 140536644177840 -> Task SumTask was aborted
[  20.049s] [P PARALLEL] ■ Finished -> 140536644177840
[  20.052s] [P PARALLEL] ⛔ Aborted -> 140536644181152
[  20.052s] [P PARALLEL] ❌ 140536644181152 -> Task SumTask was aborted
[  20.053s] [P PARALLEL] ■ Finished -> 140536644181152
[  20.054s] [P PARALLEL] ⛔ Aborted -> 140536644184320
[  20.054s] [P PARALLEL] ❌ 140536644184320 -> Task SumTask was aborted before it started
[  20.055s] [P PARALLEL] ■ Finished -> 140536644184320

# Submit x5 (Process parallel) + kill
[  23.359s] [P PARALLEL] Submitted -> 140536644181488
[  23.377s] [P PARALLEL] Submitted -> 140536644185376
[  23.390s] [P PARALLEL] Submitted -> 140536644006208
[  23.413s] [P PARALLEL] Submitted -> 140536644183696
[  23.425s] [P PARALLEL] Submitted -> 140536644181056
[  23.426s] [P PARALLEL] ▶ Started -> 140536644181488
[  23.427s] [P PARALLEL] ▶ Started -> 140536644185376
[  23.427s] [P PARALLEL] ▶ Started -> 140536644006208
[  23.429s] [P PARALLEL] ▶ Started -> 140536644183696
[  25.652s] Kill (process only): accepted by 5/5 tasks
[  25.653s] [P PARALLEL] 💀 Killed -> 140536644181488
[  25.654s] [P PARALLEL] ❌ 140536644181488 -> The task was killed
[  25.654s] [P PARALLEL] ■ Finished -> 140536644181488
[  25.655s] [P PARALLEL] 💀 Killed -> 140536644185376
[  25.655s] [P PARALLEL] ❌ 140536644185376 -> The task was killed
[  25.656s] [P PARALLEL] ■ Finished -> 140536644185376
[  25.656s] [P PARALLEL] 💀 Killed -> 140536644006208
[  25.657s] [P PARALLEL] ❌ 140536644006208 -> The task was killed
[  25.657s] [P PARALLEL] ■ Finished -> 140536644006208
[  25.658s] [P PARALLEL] 💀 Killed -> 140536644183696
[  25.658s] [P PARALLEL] ❌ 140536644183696 -> The task was killed
[  25.659s] [P PARALLEL] ■ Finished -> 140536644183696
[  25.659s] [P PARALLEL] 💀 Killed -> 140536644181056
[  25.660s] [P PARALLEL] ❌ 140536644181056 -> The task was killed before it started
[  25.660s] [P PARALLEL] ■ Finished -> 140536644181056
image

AI Disclosure

  • AI tools claude used for generate the code

Details

🤖 Generated with Claude Code

Subprocess execution

  • Execute subprocess tasks with pebble.ProcessPool (new dependency) instead of ProcessPoolExecutor: a killed worker no longer breaks the pool.
    • EwoksExecutor raises TypeError for a concurrent.futures.ProcessPoolExecutor.
    • Orange-Spectroscopy uses pebble for the same reason: Peak Fit kills a running fit in a pebble.ProcessPool worker.
  • The worker installs its SIGINT handler only while the task executes. Elsewhere, pebble's workers ignore SIGINT.
  • While the task executes, its progress goes through a sender thread in the worker (at most 100 pending values), so an abort never interrupts a queue call.
    • The progress an aborted task did not send yet is dropped.

Stopping a running task

  • Rename TaskFuture.abort/aborted → request_stop/stop_requested, and the aborted signal → stopRequested (not released yet).
    • Cooperative: the task decides whether and how to stop.
    • The stopRequested signal is emitted with the completion.
  • Add TaskFuture.abort(): interrupt like Ctrl-C. The task can clean up and the worker survives. Subprocess only.
  • Add TaskFuture.kill(): terminate the worker. No cleanup, the pool replaces the worker. Subprocess only.
  • Separate aborted and killed signals, and TaskFuture.aborted()/killed().

Queued and finished tasks

  • A queued task (waiting for a free worker) never starts after cancel(), request_stop(), abort() or kill() (abort() and kill(): process pool only). Its result() raises:
    • CancelledError after cancel()
    • TaskStoppedError after request_stop()
    • TaskAbortedError after abort() or kill()
  • A task that is prevented from starting never emits aboutToStart or started.
  • Several calls on the same queued task: only the first counts, the others return False.
  • On a finished task all four do nothing and return False.

Signal order

  • The completion signals are emitted after the start and progress signals, also after kill() from the GUI thread.
    • Except when a slot connected with Qt.DirectConnection delays the relay thread or calls kill().
  • Every submitted execution ends with finished: a cancelled task emits cancelled and finished (the widget no longer stays pending).
  • A failed submit() no longer leaves the executor busy.
    • When it fails after the pool accepted the task, the task never starts and the execution ends with cancelled and finished.

Documentation

  • concurrency.rst: a contract for widget authors and corrected diagrams for maintainers.
  • New glossary.

Windows crash at exit

  • Fix a random crash (access violation in PyQt's exit cleanup) after the test session on Windows: the ewoksorange_qtapp teardown deletes the remaining Python-owned Qt objects.

Tests and demo

  • Tests cover cancel, stop, abort and kill on queued, running and finished tasks.
  • Executor demo script: cancel, request stop, abort and kill buttons.

@codecov

codecov Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

@woutdenolf woutdenolf changed the title Use Task.request_stop and Task.stop_requested Use the ewokscore stop API and rename abort to stop Oct 14, 2026
@woutdenolf woutdenolf changed the title Use the ewokscore stop API and rename abort to stop Use the ewokscore stop API, rename abort to request_stop and add a real abort Oct 14, 2026
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 3 times, most recently from bd36fbe to b30ecf5 Compare October 14, 2026 04:49
@woutdenolf
woutdenolf marked this pull request as draft October 14, 2026 05:24
@woutdenolf woutdenolf changed the title Use the ewokscore stop API, rename abort to request_stop and add a real abort Use the ewokscore stop API, execute subprocess tasks with pebble, add abort and kill Oct 14, 2026
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 6 times, most recently from 625bc0f to 3d4fbd7 Compare October 14, 2026 06:10
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 2 times, most recently from f91d853 to f720b36 Compare October 14, 2026 15:06
@woutdenolf woutdenolf changed the title Use the ewokscore stop API, execute subprocess tasks with pebble, add abort and kill Rename TaskFuture.abort to request_stop, execute subprocess tasks with pebble, add abort and kill Oct 14, 2026
return self._denial


class TaskController(ABC):

@woutdenolf woutdenolf Oct 14, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Review tip: start by reading the docstrings of these changed class methods.

…op_requested

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 10 times, most recently from 5f7266e to 3ef0155 Compare October 15, 2026 06:23
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 2 times, most recently from 94942ee to 67f93e0 Compare October 15, 2026 06:29
woutdenolf and others added 2 commits October 15, 2026 08:33
…ture.kill, and never start a queued task that is stopped, aborted or killed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cts to avoid a crash at exit on Windows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 3 times, most recently from e271965 to 62bd8ba Compare October 15, 2026 07:16
…nd every execution with finished, and release a failed submission

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch from 62bd8ba to 151dcea Compare October 15, 2026 07:24
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch from 151dcea to 91ae215 Compare October 15, 2026 07:26
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch 6 times, most recently from 58007eb to 08c86f3 Compare October 15, 2026 13:05
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch from 08c86f3 to 7d07c0e Compare October 15, 2026 13:23
@woutdenolf
woutdenolf marked this pull request as ready for review October 15, 2026 13:57
@woutdenolf
woutdenolf requested a review from a team October 15, 2026 13:57
@woutdenolf
woutdenolf force-pushed the use_task_request_stop branch from 7d07c0e to 4ba8ca5 Compare October 15, 2026 15:40
@payno

payno commented Oct 19, 2026

Copy link
Copy Markdown
Member

I have trouble reviewing the PR to be honest (I am a human 🤣 ). Even with the tip.

This is too massive for me. I tried twice and even if a lot of it is tests I fail to review.
I understand the idea of moving to pebble but there is a lot of details I can't really properly follow because the changes touch several areas at once.

the first commit already reworks several things. Could we split this PR into several smaller ones (that can be on top of each others) - AI can probably do it ?

Here is a suggestion:

  • One with qt test fixes stuff (8b5f185, 62a026d)
  • One with renaiming: 329efdf
  • One for adding the startGate and the two queues (controller→runner and runner→controller.).
  • Moving to pebble (and potentially the kill)

@woutdenolf woutdenolf added this to the v6.0.0 milestone Oct 19, 2026

This branch has not been deployed

No deployments
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.

2 participants