Ctrl+C famously terminates a program. By default in Go any running code is immediately stopped. But sometimes, you want to do a bit of clean-up before the program ends and this is especially elegant if you use the context.Context package.

Pressing Ctrl+C actually causes a signal to be sent to your program; the SIGINT signal to be precise — referred to in Go as os.Interrupt .

If you use context.Context to handle cancellation throughout your program, then calling the CancelFunc in the main function can trigger a graceful cascade of shutdown across your program.

The following code illustrates how to do this:

First, we create a global context with context.Background before using context.WithCancel to get a cancellation function: we can call this function to cancel the operations of the program.

Next, we trap the SIGINT signal so we can run some code in response to it. The signal.Notify function asks that any of the specified signals (in our case just os.Interrupt ) be sent down the channel c rather then just immediately terminating the program.

We then defer a clean-up function that will run when the function is finished; it stops receiving the signals (via signal.Stop ) and calls the cancel function to cancel the context.

In a separate goroutine (notice go func() ) we then use select to wait for either a signal on the c channel, or for the context itself to finish. Execution will block here waiting for one of those two things to happen before continuing. If we receive a signal on the c channel, then we cancel the context by calling the cancel function. In the case that the context cancels for other reasons (like a timeout, or whatever) this goroutine is cleaned up as the <-ctx.Done(): case will hit and return.

Multiple calls to cancel are ok, after the first one they’re just no-ops.

Thanks to Sameer and Quentin for their reviews.

What next?

Check out Francesc Campoy’s video about how to use context: