-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
proposal: context.WithSignal #21521
Comments
I'd rather have that in the |
What happens when many calls to WithSignal occur in the same context tree?
This feels weird. Context is request scoped but signals are process scoped.
…On Sat, 19 Aug 2017, 01:54 Ahmed W. ***@***.***> wrote:
I'd rather have that in the signal package.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#21521 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAAcA-G5Nep4WNHIqkNaHaldEaLJwGWBks5sZbOcgaJpZM4O7pnV>
.
|
I use context for commands too, and more and more packages use it for graceful shutdown, even outside of request scope - so it makes sense to me.
… On 19 Aug 2017, at 02:57, Dave Cheney ***@***.***> wrote:
What happens when many calls to WithSignal occur in the same context tree?
This feels weird. Context is request scoped but signals are process scoped.
On Sat, 19 Aug 2017, 01:54 Ahmed W. ***@***.***> wrote:
> I'd rather have that in the signal package.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <#21521 (comment)>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AAAcA-G5Nep4WNHIqkNaHaldEaLJwGWBks5sZbOcgaJpZM4O7pnV>
> .
>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
...but it does feel weird too, I know what you mean.
… On 19 Aug 2017, at 02:57, Dave Cheney ***@***.***> wrote:
What happens when many calls to WithSignal occur in the same context tree?
This feels weird. Context is request scoped but signals are process scoped.
On Sat, 19 Aug 2017, 01:54 Ahmed W. ***@***.***> wrote:
> I'd rather have that in the signal package.
>
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub
> <#21521 (comment)>, or mute
> the thread
> <https://github.com/notifications/unsubscribe-auth/AAAcA-G5Nep4WNHIqkNaHaldEaLJwGWBks5sZbOcgaJpZM4O7pnV>
> .
>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I agree that signal.NotifyContext makes more sense than context.WithSignal.
…On Sat, Aug 19, 2017 at 1:36 PM Mat Ryer ***@***.***> wrote:
...but it does feel weird too, I know what you mean.
> On 19 Aug 2017, at 02:57, Dave Cheney ***@***.***> wrote:
>
> What happens when many calls to WithSignal occur in the same context
tree?
>
> This feels weird. Context is request scoped but signals are process
scoped.
>
> On Sat, 19 Aug 2017, 01:54 Ahmed W. ***@***.***> wrote:
>
> > I'd rather have that in the signal package.
> >
> > —
> > You are receiving this because you are subscribed to this thread.
> > Reply to this email directly, view it on GitHub
> > <#21521 (comment)>,
or mute
> > the thread
> > <
https://github.com/notifications/unsubscribe-auth/AAAcA-G5Nep4WNHIqkNaHaldEaLJwGWBks5sZbOcgaJpZM4O7pnV
>
> > .
> >
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or mute the thread.
>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#21521 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AJSK3X0Li_Sy9y2ttGyBLFLxUk1bPIX0ks5sZx0GgaJpZM4O7pnV>
.
|
To echo Dave Cheney's point: Context is request scoped but signals are process scoped. I'm not sure this proposal makes sense at all. |
My use case, and I assume others, is that I have a program that starts a series of goroutines that need cancellation, it also calls functions which accept contexts that allow me to terminate them. All of these operations are passed a context in order to cancel them, having the context derived from a single context created at application start with a cancel function similar^ to what's described by the OP. When my program needs to exit, the process manager sends a signal, which is caught by the main package and the top level context is cancelled, propogating this to the goroutines and stopping any long running functions (http requests or commands). WaitGroups then wait until everything has finished successfully and then the program terminates. This is why I don't understand the comment ^ The OP's proposal is correct, my implementation is not, so I'm in favour of adding this to |
Is there any reason why context cannot be used at a process level? Obviously the values aren't useful (as far as I can tell), but the cancellation mechanism let's you gracefully shut things down when a program is terminated early, which is very nice.
There are a growing number of packages that use context for cancellation in non-request contexts (anecdotal). It does seem to be becoming the de facto way to gracefully stop things. Has anyone else noticed that?
If context should only be request scoped, then we should start to communicate that. If not, then cancelling with a signal is going to become very common practice.
|
If several packages handle signals for each packages, and using them, it won't work expectedly. This proposal may provide wrong usages to the developers of third-packages. |
Yeah, this would only ever be used in one place, in a command. Probably in func main.
I don't think packages themselves should be trapping signals.
|
The process is a request from the user to the OS. For example, if I have a cronjob that makes network requests that result in DB transactions, I want to be able to kill it gracefully (and, perhaps more importantly in this example, to set a process-wide deadline if it cannot finish in a reasonable time so it doesn't spin all night). Having the process be the root of the context tree makes a lot of sense in that situation. It may not always make sense, certainly, but there's nothing unreasonable about this. I am not convinced that it belongs in stdlib. An example in the docs, a wiki page, a blog post explaining the subtleties involved, or a third-party library all seem reasonable, though. |
I think having this in the If anything, I think this issue shines a light on the messaging problem around "context". Should it be used for cancellation? Should it be used for value passing? What is a "request"? If the pattern of capturing signals and canceling a context from within the |
I'm not convinced we want to encourage tying contexts to signals in this way, as described above. I appreciate that the code was difficult to get right but all of it was about interacting with the os/signal package, not context. It sounds like maybe we haven't gotten the os/signal API right, or at least easy enough to use. (And that's entirely believable because signals are asynchronous events and those are just hard in general.) In Mat's post:
The part using context is really just one line: It seems like there is a general problem here that we should solve instead of solving the very specific (and arguably not too useful) one of triggering a context cancellation when an incoming signal arrives. |
Also, in |
I would prefer to have an example of correct usage in documentation instead of a new method. |
Good point for me. Maybe is it better to hang the logic outside the context package? I think it is a more flexible way. As an example what I use: For instance: middleware := func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := WithInterrupter(r.Context(), Combine(
WithSignal(os.Interrupt),
WithEvent(event.UserDeleted, event.UserBanned),
WithTimeout(service.SLA),
WithDeadline(startup.Ruin),
))
handler.ServeHTTP(w, r.WithContext(ctx))
})
} Call the global := func(critical time.Duration, handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), critical)
defer cancel()
handler.ServeHTTP(w, r.WithContext(ctx))
})
}
search := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// config.SearchTimeout < critical, maybe config.SearchTimeout << critical
ctx, cancel := context.WithTimeout(r.Context(), config.SearchTimeout)
defer cancel()
result := doSearch(ctx)
// write response
})
http.Handle("/search", global(config.SLA, search)) But what about |
This is technically a duplicate of #16472, isn't it? Not saying it can't be reevaluated, though. |
I recently built something on app engine using manual scaling, and semi-infinite running requests that do background tasks. This is another example where, while technically request scoped, it behaves much more like a command.
… On 5 Sep 2017, at 21:35, DeedleFake ***@***.***> wrote:
This is technically a duplicate of #16472, isn't it?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
I believe that the general problem here is closely related to the one in #16620: function calls and channel operations are closely related, but it is often difficult, tedious, or inefficient to convert between the two. You could imagine a generic (#15292) API for converting channel operations to function calls. package chanfunc
func Receive(c <-T, f func(T)) {
for v := range c {
f(v)
}
} Then the boilerplate here would look like: func main() {
// trap Ctrl+C and call cancel on the context
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
chanfunc.Receive(c, func(os.Signal) { cancel() })
doSomethingAwesome(ctx)
} A non-generic version of that would be to build the composition of package signal
func NotifyFunc(func(os.Signal), os.Signal...) package main
func main() {
// trap Ctrl+C and call cancel on the context
ctx, cancel := context.WithCancel(context.Background())
signal.NotifyFunc(func(os.Signal) { cancel() }, os.Interrupt)
doSomethingAwesome(ctx)
} |
This may not be the best solution, but this is definitely a pain point, and is definitely worth further discussion. |
I wrote above:
I think we should decline this proposal and leave open the question of addressing os/signal more generally. If anyone wants to make specific concrete proposals about the latter in new issues, please do. |
A common design pattern when building applications is to trap signals, such as
CTRL-C
, and gracefully close down the application.One might think this pattern is fairly straightforward and easy to implement. To illustrate the difficulties in getting this pattern correct, let's look at the following example.
In June, @matryer, wrote a blog post demonstrating how to capture signals in your application and use them to gracefully shutdown an application. This post resulted in a fantastic thread on Twitter with @Sajmani and @quentinmit pointing out the mistakes in Mat's code that demonstrate the subtleties in getting the implementation correct .
I propose that this type of cancellation, by signals, should be a part of the
context
package, as it follows the same idea as cancellation after a certain time, or by a deadline. Not only does this solve the problems with capturing signals that most people make, but, I believe, is another great use case for cancellation via contexts.An example of this addition to the context package would work is as follows:
And a proposed implementation of the
WithSignal
function.The text was updated successfully, but these errors were encountered: