Description
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:
func main() {
ctx, cancel := context.WithSignal(context.Background(), os.Interrupt)
defer cancel()
select {
case <-ctx.Done():
fmt.Println("thanks for stopping me")
}
}
And a proposed implementation of the WithSignal
function.
func WithSignal(ctx Context, s ...os.Signal) (Context, CancelFunc) {
ctx, cancel := WithCancel(ctx)
c := make(chan os.Signal, 1)
signal.Notify(c, s...)
go func() {
select {
case <-c:
cancel()
case <-ctx.Done():
cancel()
}
signal.Stop(c)
}()
return ctx, cancel
}