Skip to content

Instantly share code, notes, and snippets.

@arthur-tacca
Created March 14, 2024 12:30
Show Gist options
  • Save arthur-tacca/2ef249eef1c5e41e05620ddd842a38d7 to your computer and use it in GitHub Desktop.
Save arthur-tacca/2ef249eef1c5e41e05620ddd842a38d7 to your computer and use it in GitHub Desktop.
Demo of trio task waking without exception from `await trio.something()` despite being in a recently-cancelled cancel scope
# Question: if we cancel a scope, and another task is active in that scope, is it *guaranteed* that
# it will raise CancelledError when it next wakes? (Even if it is already queued to wake for some
# other reason.)
#
# The answer, at least in practice, is no: in this example, send_and_cancel() sends some data to
# a memory channel then cancels a scope, and then receive() (which is running in that scope) wakes
# with the first value that was sent.
#
# On my computer, this code produces the following output:
#
# Python 3.12.1 | packaged by Anaconda, Inc. | (main, Jan 19 2024, 15:44:08) [MSC v.1916 64 bit (AMD64)]
# Trio 0.24.0
# main(): running
# receive(): starting
# receive(): waiting
# send_and_cancel(): starting
# send_and_cancel(): values sent
# send_and_cancel(): cancelled
# receive(): got 1; cancelled: True
# receive(): waiting
# receive(): exception Cancelled()
# main(): done
#
import sys
import trio
async def send_and_cancel(snd, cnc):
print("send_and_cancel(): starting")
await trio.sleep(0.5)
snd.send_nowait(1)
snd.send_nowait(2)
snd.send_nowait(3)
print("send_and_cancel(): values sent")
cnc.cancel()
print("send_and_cancel(): cancelled")
async def receive(rcv, cnc):
print(f"receive(): starting")
try:
for _ in range(2):
print("receive(): waiting")
val = await rcv.receive()
print(f"receive(): got {val}; cancelled: {cnc.cancel_called}")
print(f"receive(): done")
except BaseException as e:
print(f"receive(): exception {e!r}")
raise
else:
print(f"receive(): finished no exception")
async def main():
print("Python", sys.version)
print("Trio", trio.__version__)
snd, rcv = trio.open_memory_channel(max_buffer_size=5)
async with trio.open_nursery() as n:
n.start_soon(receive, rcv, n.cancel_scope)
n.start_soon(send_and_cancel, snd, n.cancel_scope)
print("main(): running")
print("main(): done")
trio.run(main)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment