-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
RFC: more reliable & extensible ^C REPL interrupt #14032
Conversation
…n hack Instead of making task_done_hook aware of the REPL specifically, this gives clients (such as the REPL) the ability to register a handler for any unhandled exception. In reality, the only unhandled exceptions are those that occur when trying to run task_done_hook or early on the root task. Accordingly, the most like error is InterruptException, but not exclusively. This approach allows the REPL to print an error and return to a prompt, even when the Task state of eval_user_input is too inconsistent or unknown to be called directly. If the last-chance exception handler returns or throws an error, that that error is then considered to be finally fatal. The return value when setting the exception handler allows the user to chain and reset the handler.
backend.in_eval = false | ||
backend.ans = value | ||
put!(backend.response_channel, (value, nothing)) | ||
eval(Main, :(ans = $(Expr(:quote, value)))) |
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.
Did you verify that #6763 doesn't crop back up with this change?
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.
i hadn't, but trying it now it seems to work as expected. it looks like i need to delete the above comment too.
i was modifying this on the implementation of the similar function in client.jl:
07a0c09
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.
the changes to this function fix #13955
For IJulia, I really want SIGINT to only go to a certain task (the one executing user code); is that possible with this PR? |
no, this would still kill every running task until it eventually managed to strike the user one. i think i may be able to put together a more general solution for that also helps with your IJulia case |
Will this PR fix JuliaLang/Distributed.jl#34 ? |
it should help. although calling SIGINT is usually a bad idea regardless. |
Why? If we are handling Ctrl-C is the REPL shouldn't |
Ref #2622 |
That is a different issue - the argument there is about state maintained in interrupted external libraries. My question is shouldn't Ctrl-C at the REPL and an external signal sent have the same behavior? |
It does have the same behavior, just that currently unless you know the process you are interrupting is in a safe state (which is hard due to the asynchronous nature of signals) it can cause crashes. |
Practically it doesn't. On a fresh julia REPL session with no code run, Ctrl-C does nothing. Sending an external
This was not the behavior a while back. |
Seems that in REPL we actually handle the key event directly without going through the signal and that's why the behaviors are different. I suppose this PR fixes the handling of SIGINT when no code is running (if it doesn't, it shouldn't be hard to fix either) and what @vtjnash meant by a bad idea is #2622. |
From the comment it appears that all active tasks are killed. Shouldn't we only kill the currently running task? As an example, the parallel code base has a background task initiated at Julia startup that pushes distributed gc messages to other workers. A Ctrl-C / SIGINT should not kill this task (and other user background tasks typically started with a |
currently running Task is transiently ill-defined -- there's no such thing as a background task, just currently running task. it's just as likely that you might happen to deliver the signal to the gc task or the client message handling loop and terminate it instead of some user task. |
Would it help if we could mark tasks to ignore SIGINT ? This way tasks that wait on a socket (in multi.jl), or the gc task would call, say, |
that would be equivalent to making sigatomic task-local, which sounds like a good idea to me. we should have throwing sigint set that flag (actually, any error) and reset it in the catch handler, so we don't try to throw a second error while unwinding one error. |
Is this relevant in any way for #35524 or is it really old and can be closed? |
Closing this as old, but please reopen if still something we can use. |
Instead of making task_done_hook aware of the REPL specifically,
this gives clients (such as the REPL) the ability to register a
handler for dealing with any exception that would otherwise not have a handler.
For example, the following code is valid. And while not recommended in this form,
this code is essentially a simple produce/consume pattern.
If you wait a few seconds, it will even finish.
However, on v0.4, hitting ^C might either accomplish nothing or infinitely cause a stack overflow segfault.
with this change, hitting ^C gets back to the REPL immediately.
This can handle other sorts of exceptions, although
in reality, the only unhandled exceptions are those that occur
when trying to run task_done_hook or early on the root task.
Accordingly, the most likely error is InterruptException,
but not exclusively.
This approach allows the REPL to print an error and return to a prompt,
even when the Task state of eval_user_input is too inconsistent or
unknown to be called directly.
If the last-chance exception handler returns or throws an error,
that that error is then considered to be finally fatal.
The return value when setting the exception handler allows the user
to chain and reset the handler.