-
-
Notifications
You must be signed in to change notification settings - Fork 342
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
Gracefully shutting down nurseries on KeyboardInterrupt #143
Comments
This is partly a matter of luck –
What do you mean that children nursery scopes don't get a Cancelled exception? Once the outermost nursery has its cancel scope cancelled, then everything (children, grandchildren, great-grandchildren, ...) below it should get a Oh, actually, I have a guess – there are several grandchildren that get Cancelled exceptions, and those propagate into their parent, one of the first-level children. And since it's supervising multiple grandchildren and they all crashed, it bundles their exceptions up into a
As noted above, catching
So the fundamental challenge here is that there isn't really any way to know "why" you were cancelled – maybe the peer has totally disappear and a timeout finally expired, maybe there was a KeyboardInterrupt and the program is shutting down, ... and this is intentional, because it turns out that once you allow multiple "types" of cancellation it makes life really messy really fast. What if a cancel scope had its timeout expire and someone called One option is discussed in that doc section I linked above: while a scope that's been cancelled prevents all blocking operations inside it by default, you can disable this by wrapping your "say goodbye" code in a new cancel scope with its Alternatively, if you really want control-C specifically to be treated differently than other kinds of shutdown/crash/cancellation events, you absolutely can set up a custom handler for it that does whatever clever thing you want. For example, you could set a global flag trio_SIGINT_handler = signal.getsignal(signal.SIGINT)
def my_SIGINT_handler(signo, frame):
# set our flag:
global got_KeyboardInterrupt
got_KeyboardInterrupt = True
# then run trio's normal handler:
trio_SIGINT_handler(signo, frame)
signal.signal(signal.SIGINT, my_SIGINT_handler) Does any of that help? It's kind of hard to give specific advice without knowing what you're ultimately trying to do :-) |
Thanks for the detailed response. My ultimate use case isn't all that mysterious (#124 :-) Basically I want to gracefully send the close message to each WebSocket connection when shutting down the server. As an async/trio newbie, I am still struggling to wrap my head around the idea that the child nurseries aren't nested in the typical stack sense, ready to catch any exception before a parent nursery could possibly see it (eg if all of my leaf nurseries wrap It was the In As always, thanks for your patience as I try to figure this stuff out. |
I've created a new issue for adding |
I am experimenting with nested nurseries to handle multiple tasks associated with each incoming connection. I would like to send a final message on each connection as the nursery exits. I've run some tests and noticed a few things that seem to make this difficult.
KeyboardInterrupt
triggers atrio.Cancelled
in tasks (which can be caught) but not in children nursery scopes. If we push enough state down into the tasks this might be workable, but it would be easier to handle at the parent/nursery level.finally
seems the only way to trigger code as we leave a child nursery but as soon as we await, we exit (presumably because the scope has been cancelled)What is the best way to trigger "awaitable" code (eg send one last message) as we exit a child nursery from a KeyboardInterrupt?
The following code illustrates the issue (without using any networking :-). I would like to trigger my shutdown code in the "inner" scope:
The text was updated successfully, but these errors were encountered: