Created
March 14, 2024 12:30
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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