-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Improve Process.on_interrupt
for handling more process termination signals
#13983
Comments
#13694 is a proposal to enhance |
Why can't #13694 be implemented as an overload? It would likely mean having two handler types but the current could just be deprecated, and the transition to the new signature is minor. |
Unfortunately, it's impossible to overload on the type of a block parameter. |
I could modify my pull request to implement |
Turning |
I'm wondering if it could be possible to accept |
I've updated the pull to check the arity of the proc and call it appropriately - no macros required |
Rethinking about this, the other types of events are, well, not interrupts. The name |
If you want to get prescriptive, once you throw Windows into the mix are any of the windows events interrupts? GNU docs group them as "termination signals" https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html A "process interrupt" seems like a generic enough term to group the behavior though |
Ctrl+C is a user-initiated interrupt in a general sense; the spec runner chooses to finish early, other things can choose to ignore it, effectively cancelling the interrupt. With a termination request you cannot cancel the termination in the same manner. Right now I would do something like this: class Process
# handles `INT`, overrides `on_shutdown`
def self.on_interrupt(& : ->)
end
# handles `HUP` and `TERM`
# also handles `INT`, until `on_interrupt` is called
def self.on_shutdown(& : ->)
end
# ignores `INT` as if by installing an empty handler
# does not affect `HUP` and `TERM`
def self.ignore_interrupts!
end
# removes the `on_interrupt` handler; if `on_shutdown` had been called before,
# that handler will handle `INT` again after this
def self.restore_interrupts!
end
# how do we reset the `on_shutdown` handler? do we even need it?
end |
I would argue that ctrl-c is user-initiated termination - "I want to stop this process so I can launch another from the same terminal" that can be optionally ignored by the application as the user can always close the terminal window if they really want to stop the process. At least in my experience this is the more common workflow and use case for the signal |
To throw in another data point, The term "interrupt" has different semantics on different levels. A very narrow application would be And I think a single callback which receives the type of interrupt as a parameter is better than registering separate callbacks for every type. It makes it easy to group the same responses to different interrupt types. |
But |
I agree on this specific use case for |
Following this discussion I suppose we can agree to move forward with the general API? There is however another issue with backwards compatibility of program-level semantics. Signal::HUP.trap do
STDOUT.puts "Signal HUP received"
end
Process.on_interrupt do
STDOUT.puts "Interrupt"
end
sleep With current Crystal, the program reacts to SIGHUP with the explicit HUP handler. This is a silent breaking change, which is not good. It might even be preferable to have a hard change in the API to have it explicit. Perhaps the best path forward would really be to add this as a different method with a unique signature and keep |
How about making In a nutshell: def on_interrupt(force : Bool, & : ->)
..
raise SignalHandlerAlreadyAttached.new("") unless Signal::HUP.trap(..).nil? || force
..
end I'm coming from #13803 here - i.e. I think just blindly The above signature would continue to allow that, but force everybody |
@straight-shoota would something like this maybe a way forward:
I feel like the ruby way of just returning any previously defined callback is probably a good way forward as it's simple. In terms of overriding I think that's fine as long as it's documented. Being able to check if there are handlers in place allows devs like @m-o-e to achieve the desired outcome Signal::HUP.trap do
STDOUT.puts "Signal HUP received"
end
existing_callback = Signal::HUP.trap_handler?
Process.on_terminate do
existing_callback.try &.call
STDOUT.puts "Interrupt"
end |
updated the pull request to do the following:
devs can chain signals on unix using #14126 if they want to handle something independently whilst also using |
Currently
Process.on_interrupt
installs a handler for process interrupts. On Unix systems, this corresponds toSIGINT
, on Windows itsCTRL_C_EVENT
andCTRL_BREAK_EVENT
.Handling other termination requests currently requires explicit, platform-specific code, as mentioned in #13694 (comment)
Having a generic stdlib API would help writing portable code.
On Unix we typically want to handle INT, HUP and TERM more or less equivalently. And the equivalents on Windows are similar.
There should be a simple default mechanism for handling all of these reasons the same way. But for some use cases it may be necessary to differentiate between them,
The reasons can be categorized and mapped as follows (thanks @stakach):
SIGINT
CTRL_C_EVENT
,LibC::CTRL_BREAK_EVENT
SIGHUP
CTRL_CLOSE_EVENT
SIGTERM
CTRL_LOGOFF_EVENT
,LibC::CTRL_SHUTDOWN_EVENT
Now we could have a combined handler for all of them, and a means to select different behaviour when it's called. Alternatively, there could be separate handlers, for each one. But then we'd need a simple mechanism to set up a handler (the same one) for all of them.
A tangentially related issue is #13803
The text was updated successfully, but these errors were encountered: