-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
os/signal: add func Ignored(sig Signal) bool #22497
Comments
\cc @bcmills |
Worth noting: on Unix at least, asynchronous and real-time signals have a lot of subtleties in multi-threaded applications which are not captured in this proposal. I think most application developers don't care about those issues and just want to know what will happen when someone says |
The API that you propose is inherently racy. The
It is only safe to replace C signal handlers while the program is known to be single-threaded: the POSIX signal API does not provide a compare-and-swap flag, so there is no way to replace a signal handler without potentially racing with other libraries doing the same, and it is not in general safe for any library to assume that it has exclusive control over the program's signal handlers (see #20400). The call to See also #20748 — another race involving signal disposition and registration — and my comments on change 42830. If a library correctly avoids replacing handlers after threads have been started, any handlers it has registered should enable and disable themselves by setting atomic variables within the handler library, not by registering and unregistering the handler itself. (The state may be even more ambiguous once #19465 is addressed.) So the proposed The proposed |
Honestly, I think we should discourage the use of In contrast, |
There absolutely is a potential for races here, depending on implementation details. However that's neither here nor there for the main use cases. There are cases where an application might want to handle SIGHUP or SIGINT even if the program started out ignoring the signals. There are cases where handlers might behave differently depending on what the disposition started out as. Perhaps the concerns about concerns about race conditions could be mitigated without loosing the main useful functionality by limiting the scope of the API to determining what the "default" behavior for a signal is before any application handlers were registered. That is, how does the go runtime treat the signal, and was it ignored when the runtime started up. |
About the only thing you can't do with the |
Granted having Notify be a no-op if the signal is ignored would solve 95% of use cases I can imagine. However I can imagine an application which would wish to do something with SIGINT even in the "ignored" case, for example write something to a log, but exit iff the signal was not previously ignored. It's also useful to be able to inspect signal disposition when writing unit tests for code which is supposed to set up handlers. I could also imagine cases where it be useful to be able to programatically enumerate which signals are handled by the runtime (e.g. SIGILL) and should not be handled by user code. |
I'd also be worried about the compatibility implications of changing Notify to do nothing for ignored SIGHUP, in case someone was relying on being able to catch it. Giving the user the tools they need to decide for themselves seems safer in that regard. |
If you know that your handler is the only one in the process, you can forward the signal yourself (modulo #19326): c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
go func() {
runtime.LockOSThread()
for {
sig := <-c
signal.Stop(c)
syscall.Tgkill(syscall.Getpid(), syscall.Gettid(), sig)
signal.Notify(c, syscall.SIGINT)
}
}() If you don't know that your handler is the only one in the process, trying to achieve that sort of behavior via the You could imagine some entirely different API for forwardable signals in Go, but it's not obvious to me how to do that without producing an API that is prone to deadlocks, fails to interoperate with C handlers, and/or fails to interoperate with the existing |
That one is easier: write your tests to execute the Go code as a subprocess using a known signal mask. |
I don't see the point of But I do see the point of something like |
What if we just add to os/signal
? |
I think it's true that it's reasonable to ask application code to keep track of the current status if it needs to, especially since it's potentially racy. But being able to ask whether it was ignored at runtime startup is useful and not racy. |
@adam-azarchs Right, you can use @rsc's proposed |
@adam-azarchs, does func Ignored (in #22497 (comment)) solve your problem? |
Yes, it does. My only concern about it is more around documentation of the various possible races, but it does solve the problem. |
Proposal accepted then, scoped to the func Ignored described above. CLs welcome for Go 1.11. |
Change https://golang.org/cl/98835 mentions this issue: |
If anyone want to pick this up, that CL above was abandoned. |
Change https://golang.org/cl/108376 mentions this issue: |
Finally got around to getting my employer to sign the CLA. I've taken a crack at resolving this, at least on unix systems. The state of signal functionality in general on windows/nacl/plan9 seems to leave something to be desired. |
Summary
Generally speaking, when a Go process receives a signal it will either be handled (by the runtime or by application code), ignored, or will terminate the process. There are several scenarios where it is useful to know which one will happen, and also what the original behavior was for the process (e.g. what
signal.Reset(sig)
will change it to).On unix systems, the current disposition can be obtained by
runtime.getsig
and the original disposition is inruntime.fwdSig
. Neither of these are exposed to the application.Example use case
Determining whether SIGHUP is ignored (which will be the case if run through
nohup
) before registering a handler. The go runtime already does this, but there isn't a great way for application code to do it.Proposed API
Exposing the function pointers returned by
getsig
would be a bad idea. Instead, the proposal is to add to theos/signal
package a typeand methods
The text was updated successfully, but these errors were encountered: