-
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 NotifyContext function #37255
Comments
Change https://golang.org/cl/219640 mentions this issue: |
Also please notice that this change would, unfortunately, require introducing packages time and context as dependencies for package os/signal. |
Could you describe the new function in this issue, so that we don't have to figure it out from the CL? Thanks. |
The new function receives a parent context and a variadic number of signals that can be used to cancel this context. When a parent context is canceled or when one of the signals is handled, the context is canceled. Example of handling cancelation of a slow process through SIGTERM and SIGINT: func main() {
ctx, cancel := signal.WithContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
defer cancel()
// ...
slow.Run(ctx)
cancel()
// to verify if the slow function was terminated by a signal, we can use:
var sigErr signal.ContextError
if errors.As(ctx.Err(), &sigErr) {
fmt.Printf("slow function terminated by signal %q\n")
}
} On another side, we have the example shown for https://golang.org/pkg/net/http/#Server.Shutdown that, in my opinion, would not be simplified by this. |
Perhaps this should be a function of the context package instead, since it creates a context and it's pretty much all about contexts. ctx, cancel := context.Signal(context.Background(), syscall.SIGTERM, syscall.SIGINT) Not sure it's the right idea at all, just throwing it out there. |
If several packages handle same signal, and runtime does not handle those contexts equally, context.Signal won't work correctly, I think. If runtime does not handle them, |
To be clear, the runtime does already broadcast signals to anyone who has signed up to hear about them, using signal.Notify. So this could be done as a separate package without worrying about other possible uses of os/signal. context.WithCancelSignal seems like the clearest name, and it would appear right next to context.WithCancel in the docs. /cc @Sajmani for thoughts |
It sounds like maybe we have converged on |
Great! Thanks for the feedback. I'll try to send a new CL proposing an implementation for a |
Is there a plan for how this would be documented? Signals are process scoped, so any library using this feature could be problematic. And in a server I would normally catch signals at main level and propagate down, which can be easily/lazily done already by cancelling a single parent context. So using signals for local task cancellation seems only suited for interactive apps. If that's not too controversial, could it be made clear in the docs? |
I pushed a new implementation to https://go-review.googlesource.com/c/go/+/219640/1 |
I note that currently according to go/build/deps_test.go context depends only on
This is going to add a dependency of context on os and os/signal, which pulls in a lot more. I don't know that it matters much, but it seems worth mentioning. Is anybody concerned about the additional dependencies? |
I was worried too, but if you look at the full lists it doesn't add much transitively. In the current tree:
The only addition would be I'm not saying I'm happy about this, but it's arguably OK. |
If receiving the signal in goroutine and context.WithCancelSignal, I wonder how this work.
|
One possibility to avoid adding a dependency on os and os/signal is to have the context package reach into the runtime package as the os/signal package already does. One consideration about depending on the os/signal package is that the os/signal package has an init function that slightly changes how the runtime package handles signals. But we might be able to get rid of that anyhow. |
@mattn The proposed But assuming you meant to pass |
Oh, I see. |
I'm worried about the number of changes in the signal package too. I'll take a look into the runtime package Friday to see if I understand @ianlancetaylor's idea and to see if I can come up with something. |
FWIW, I think we can easily hook this into the runtime package directly (with an internal package if needed) and bypass os and os/signal, to keep the deps lite. The general trend is to avoid os where possible, and all this code only needs the runtime. No change in consensus here, so accepted. |
Not an expert in signal handling by any means, but... Why would it be bad to change the signal handling behavior when the parent context is canceled? Wouldn't also be similarly bad if it doesn't (especially if there is a way to check what signal caused the context to end)? It's not clear to me what an example would be. Instead, shouldn't NotifyContext be used first when this happens? Another thing to worry: if you want to use this for something like handling configuration reload through SIGHUP, it might be fine if you lose a signal (if you replace the context immediately to keep this going on - I'm not sure if this would be good btw). However, if you're listening to, say, SIGTERM, if a second SIGTERM arrives before you called signal.Notify again, your process might terminate before it should. |
There is already a pretty strong precedent that package signal
// ContextSignal reports which (if any) signal caused ctx to be canceled.
//
// If ctx was not canceled due to a signal, ContextSignal returns (nil, false).
func ContextSignal(ctx context.Context) (os.Signal, bool) {
sig, ok := ctx.Value(signalContextKey{}).(os.Signal)
return sig, ok
} |
@bcmills, I like it. |
@bcmills, If somewhere within a call stack, I had an operation, that accepted the context and reacted to its cancellation, I don't think I would check something related to the signal in this operation, but rather returned
Is that how |
To fix the double-signal problem, it sounds like we have to deviate from the usual context.WithCancel to require calling the cancellation function if you want to stop the captured signal behavior. That is, the docs would change to:
It remains unclear that we really need the signal details. For that, it's easy enough to write the simple code that handles the signal, records it, and cancels a context. That is, NotifyContext is meant to be more a convenience than a complete Swiss army knife. We can always add it later if we need to, but it probably makes sense to start without it. Thoughts? |
It makes sense to me. I liked the idea of calling the cancel function stop to differentiate things. |
Based on the discussion above, it seems like we've reconverged on signal.NotifyContext, documented in #37255 (comment). This seems like a likely accept (take two). |
No change in consensus (yet?) so accepted (again). |
@henvic Were you planning to adapt your CL (or a new one) based on the change to os/signal.NotifyContext? I'd be happy to help however I can, would be great to get this in for 1.16. |
@danp, thank you for the help! Hello @ianlancetaylor, might you please take a second look at the updated CL? I have sent a new patchset. Thank you! |
As previously discussed on issues such as #21521 and #16472, or on several other places handling POSIX signals through context seems to be somewhat useful (and a little bit hard to do correctly?), and I would like to propose a way to handle it by adding a new signal.WithContext function.
There's also an idea to improve the current approach to handling signals (see #21521 (comment)). I didn't give it a try yet, unfortunately. I only found the earlier proposals here after trying to write some code, so I decided to create this new issue anyway to share my idea.
People using some sort of it or talking about the subject:
The text was updated successfully, but these errors were encountered: