Thrive AI Editorial
Thrive AI Editorial · AI-assisted worked example, not a member conversation.
2026-10-07
A canceled wait is not proof that execution stopped
Cancellation affects the awaited asynchronous operation. It cannot generally force a blocking function that is already running in an operating-system thread to stop. If the blocking function is sending a request or writing to an external service, the original effect may still complete after the caller stops waiting.
The safe interpretation is outcome unknown until you can reconcile the operation. Treating every timeout as definitely failed and retrying a non-idempotent action can duplicate effects.
caller stops waiting ----- timeout response
worker thread ------------------------ finishes original work
Observe the boundary without a timing guess
Python 3.9 or newer, standard library only. An event explicitly holds the worker until after cancellation, so correctness does not depend on a tiny sleep winning a race.
import asyncio
import threading
async def main():
started = threading.Event()
release = threading.Event()
finished = threading.Event()
effects = []
def blocking_tool():
started.set()
release.wait()
effects.append("completed")
finished.set()
task = asyncio.create_task(asyncio.to_thread(blocking_tool))
await asyncio.to_thread(started.wait)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("caller stopped waiting")
assert effects == []
release.set()
await asyncio.to_thread(finished.wait)
assert effects == ["completed"]
print("worker effects:", len(effects))
asyncio.run(main())
Expected output:
caller stopped waiting
worker effects: 1
Decide which deadline you actually control
Separate an application response deadline, a network client's timeout and the remote service's execution state. A socket timeout does not establish whether the server committed the request. A timeout around a coroutine does not automatically terminate a thread it started.
Prefer native asynchronous clients where appropriate, propagate deadlines to supported dependencies, and use cooperative cancellation for work you control. Even then, already committed remote effects require reconciliation. A thread cancellation flag is not a rollback.
Use stable operation identifiers for retriable side effects. When the deadline expires, record a pending or uncertain state rather than presenting a fabricated success or a confirmed failure. Bound concurrent workers so timed-out operations do not accumulate without limit.
Tests that expose the bug
Delay completion until after the caller times out, then check whether the effect occurs. Retry using the same operation key. Verify that the UI eventually reconciles to the actual result and that shutdown does not abandon important state silently.
The example intentionally uses cancellation directly to isolate the waiting boundary; it does not implement a production timeout wrapper. Test the precise cancellation semantics of your runtime and client library. Do not infer safety from the exception type alone.
Source checked 2026-10-08: Python asyncio tasks, cancellation and running work in threads. The executable demonstration and uncertain-outcome analysis are original AI-assisted material. No model or network API is used.
Sign in to vote or contribute. Scores count actual member votes, not editorial endorsements.