-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed task group getting cancelled if start() gets cancelled #717
Conversation
I tried this out. I did If I cancel But if I cancel |
BTW in Trio, in that second case, by the time the routine Edit: yes I missed something. Trio actually holds up the destination nursery from closing until the task has finished starting, even though the task is not cancelled, so a nursery being cancelled does indeed wait on a task that has no indication that it should cancel. See python-trio/trio#1431 |
Can you at least provide a reproducing snippet of what isn't working? |
Sorry yes, the fold below contains a repro (use of aioresult isn't necessary but I found it easier to write this way). I think the thing I'm missing about Trio is that it actually can have bugs / incomplete design decisions! This other issue is arguably a second bug (I actually found it by looking at your changing and thinking ... how does the destination nursery find out when the new task is reparented to it? The answer is: it already was treated as a child.) Another way to interpret my comment: this change fixes the original bug and doesn't (to my knowledge) introduce any new bugs. Code to reproduce odd behaviours# Test for https://github.com/agronholm/anyio/pull/717
import anyio
import aioresult
futures = [None, None]
async def startable(task_status = anyio.TASK_STATUS_IGNORED):
print("startable(): starting")
try:
await anyio.sleep(1)
except BaseException as e:
print(f"startable(): during first sleep {e!r}")
raise
print("startable(): after first sleep")
try:
task_status.started("x")
except BaseException as e:
print(f"startable(): during started {e!r}")
print("startable(): about to sleep again")
try:
await anyio.sleep(1)
except BaseException as e:
print(f"startable(): during second sleep {e!r}")
raise
print("startable(): after second sleep")
async def run_first():
async with anyio.create_task_group() as tg:
futures[0].set_result(tg)
await futures[1].wait_done()
other_tg = futures[1].result()
await other_tg.start(startable)
await anyio.sleep(1)
async def run_second():
async with anyio.create_task_group() as tg:
futures[1].set_result(tg)
await anyio.sleep(2)
async def run_test(i, which_tg_cancel, when_cancel):
futures[0] = aioresult.Future()
futures[1] = aioresult.Future()
async with anyio.create_task_group() as tg:
tg.start_soon(run_first)
tg.start_soon(run_second)
await aioresult.wait_all(futures)
await anyio.sleep(when_cancel)
futures[which_tg_cancel].result().cancel_scope.cancel()
print("before sleep")
await anyio.sleep(0.2)
print("after sleep")
for i in range(2):
c : anyio.CancelScope = futures[i].result().cancel_scope
print(f"tg {i}: cancelled: {c.cancel_called}, caught: {c.cancelled_caught}")
c: anyio.CancelScope = tg.cancel_scope
print(f"tg outer: cancelled: {c.cancel_called}, caught: {c.cancelled_caught}")
print("test complete")
async def run_all():
print("\n\n\ntest 1: cancel 1st TG early")
await run_test(1, 0, 0.5)
print("\n\n\ntest 2: cancel 2nd TG early")
await run_test(2, 1, 0.5)
print("\n\n\ntest 3: cancel 1st TG late")
await run_test(3, 0, 1.5)
print("\n\n\ntest 4: cancel 2nd TG late")
await run_test(4, 1, 1.5)
anyio.run(run_all, backend="asyncio") |
Also, a nitpick: I found the comment confusing
I assumed this referred to the reason why we're performing this test at all. So we're doing this because the host task was cancelled? Hmm. In fact, it's assuming you already know why we're performing this test and is actually explaining how it's testing for it. Perhaps it could be preceded by a comment that says why we're performing the test (e.g., something like "check whether this task was cancelled while still waiting for it to start in |
I've clarified the comment now, hopefully it's okay now. |
It's not clear to me what the expected output of this script is. |
@Zac-HD would you mind rubber stamping this PR? @arthur-tacca said above that it fixes the original bug and doesn't introduce any new ones. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, fix and test both look good to me.
This change fixes the problem by special casing the situation where the Future backing
task_status
was cancelled which only happens when the host task is cancelled.Fixes #685. Fixes #710.