Closed skgbanga closed 5 years ago
Ah, the kid never really goes to the TIME_SLEEP
state.
curio > ps
Task State Cycles Timeout Sleep Task
------ ------------ ---------- ------- ------- --------------------------------------------------
1 FUTURE_WAIT 1 None None Monitor.monitor_task
2 READ_WAIT 1 None None Kernel._run_coro.<locals>._kernel_task
3 TASK_JOIN 2 None None parent
4 SEMA_ACQUIRE 1 None None kid
5 TIME_SLEEP 1 None 985.445 friend
6 TIME_SLEEP 1 None 985.445 friend
7 TIME_SLEEP 1 None 985.445 friend
8 TIME_SLEEP 10 None 0.45906 countdown
probably because aexit
of the context manager waits for the tasks to finish.
I am still curious on why the print
line never gets printed.
probably because aexit of the context manager waits for the tasks to finish.
yes it would wait for 1000 secs for all of the friend tasks and that is when parent calls await kid_task.cancel()
. At any await point, you must reckon with a curio.CancelledError
exception. here the exception gets thrown and the try/except block of kid() would never get executed.
Yes, this is being caused by the TaskGroup waiting for all children to exit. A core behavior of task groups is that spawned tasks are not allowed to outlive the lifetime of the task group.
I actually am a bit curious about the behavior though. If the kid()
task is being cancelled without the print
taking place, it means that the cancellation must be originating in the __aexit__()
method of the context manager. I suppose that's fine, but raising exceptions in the exit method of a context manager is frankly a little weird. A few other parts of Curio actively try to avoid this. For example, consider this example with cancellation control:
from curio import *
async def kid():
async with disable_cancellation():
await sleep(5)
try:
await sleep(10)
except CancelledError:
print("Kid cancelled")
async def parent():
t = await spawn(kid)
await sleep(1)
await t.cancel()
run(parent)
This prints "Kid cancelled." Hmmm. I'd need to think about this. You could probably fix it by putting the try-except around the whole TaskGroup construct.
This change of moving the try-except would probably be a good example to add to the tutorial to illustrate the behavior of TaskGroup. That is, by moving the try except, the original kid() task merely sits around watching the friends play instead of playing at the same time.
Thanks for the replies. Here is something interesting then:
If I change the kid's function to be:
async def kid():
print('Building the Millenium Falcon in Minecraft')
try:
async with curio.TaskGroup() as f:
await f.spawn(friend, 'Max')
await f.spawn(friend, 'Lillian')
await f.spawn(friend, 'Thomas')
await curio.sleep(1000)
except curio.CancelledError:
print('Fine. Saving my work.')
raise
The output is
....
We're leaving!
I warned you!
Fine. Saving my work.
Thomas going home
Lillian going home
Max going home
Leaving!
Which means that if the TaskGroup has some tasks which have some cleanup work to do ({name} going home
in this case), that can actually happen outside the async with curio.TaskGroup
. (Because Fine. Saving my work
was printed before those)
I am also curious about the example you gave:
from curio import *
async def kid():
async with disable_cancellation():
await sleep(5)
try:
await sleep(10)
except CancelledError:
print("Kid cancelled")
async def parent():
t = await spawn(kid)
await sleep(1)
await t.cancel()
run(parent)
Since kid cancelled
gets printed, it means that CancelledError
was eventually raised when kid did an await
call. e.g. if I change the kid's function to:
async def kid():
async with disable_cancellation():
await sleep(5)
print('Doing some expensive work')
try:
await sleep(10)
except CancelledError:
print("Kid cancelled")
Then Doing some expensive work
will get printed. I guess this is perhaps because the control flow has to transfer back to the loop before any action is taken, but it does feel a bit weird. I think it is more natural for CancelledError
to be thrown at the end of context manager in this case. (and thus letting all the things within the context manager to be executed without cancellation, but no more than that)
Ooh. Interesting. I think this is related to something I had noticed while debugging something else last week---namely, the use of a non-blocking cancellation in Task Groups seemed to be weird. I thought I had eliminated all uses of that, but I see that I didn't. I'll push a fix that should address this.
I can confirm that with the fix, TaskGroups
tasks' finally block is executed before the parent.
Please feel free to close this ticket, and thanks for answering the questions.
Curio Tutorial uses the following example to introduce some of its concepts.
The above code is not exactly similar to the example mentioned on the page,
kid()
function has one change.try/except
block is outside theasync with
block.The above example produces the following output:
As you can see, the line
Fine. Saving my work.
never gets printed. (All the friends got theCancelledError
exception and printed... going home
fine.Is this intential behaviour? If yes, this is slighly surprising.