Asynchronous frameworks like asyncio
, trio
, and anyio
use special exceptions to signal that a task or
operation should be cancelled. These exceptions are not typical errors indicating a logical flaw in the task but are directives for the task to
terminate its execution prematurely and perform necessary cleanup.
When a task is cancelled, the framework typically injects this cancellation exception into it. The task is expected to:
- Catch this specific cancellation exception.
- Perform any urgent and brief cleanup actions (e.g., releasing locks or other resources).
- Re-raise the cancellation exception.
If a cancellation exception is caught and not re-raised (e.g., it’s suppressed with a pass
statement, only logged, the handler returns
normally, or a different exception is raised instead), the cancellation signal is effectively "swallowed".
This prevents the framework and any calling code from knowing that the task has acknowledged the cancellation and is stopping. The task might even
continue running parts of its code after the except
block, which is contrary to the purpose of cancellation.
Properly propagating cancellation exceptions is crucial for the cooperative multitasking model these frameworks rely on.
What is the potential impact?
Suppressing cancellation exceptions can lead to significant problems:
- Unresponsive Applications: Tasks ignoring cancellation may run indefinitely, making the application unresponsive to shutdown
signals.
- Resource Leaks: Tasks not stopping properly can leave resources (files, connections, locks) unreleased, leading to resource
exhaustion.
- Incorrect State: Partial execution of cancelled operations can leave the application in an inconsistent state, risking data
corruption.
- Debugging Difficulties: Troubleshooting why tasks continue running or why shutdown fails becomes challenging.
- Broken Abstractions: Reliable cancellation is essential for async patterns and libraries; ignoring it breaks timeouts and task
groups.