Handling CTRL-C (interrupt signal) in Golang Programs

Interruptions

Recently I’ve been working on a Go program where I will need to do some cleanup work before exiting if the users press CTRL+C (thereby sending an interrupt signal, SIGINT, to the process). I was unsure of how to do this.

As it turns out, signal.Notify is the method by which this is accomplished.

Here is some sample source code:

// Code to set up some services up here...

// After setting everything up!
// Wait for a SIGINT (perhaps triggered by user with CTRL-C)
// Run cleanup when signal is received
signalChan := make(chan os.Signal, 1)
cleanupDone := make(chan struct{})
signal.Notify(signalChan, os.Interrupt)
go func() {
    <-signalChan
    fmt.Println("\nReceived an interrupt, stopping services...\n")
    cleanup(services, c)
    close(cleanupDone)
}()
<-cleanupDone

I like this example because it really illuminates the power of Go’s concurrency primitives. Instead of having to worry about complicated process or threading logic I simply abstract away the concurrency details using a goroutine and a couple of channels. In this instance, the main goroutine is blocked by an unbuffered cleanupDone channel because that is the behavior that is expected (we’ve already spun up additional goroutines earlier to do some logging and handling outside of the context of the main goroutine).

Now I can clean up after my containers when a user interrupts the terminal with CTRL+C. Awesome!

Until next time, stay sassy Internet.

  • Nathan
I want to help you become an elite engineer. Subscribe to follow my work with containers, observability, and languages like Go, Rust, and Python on Gumroad.

If you find a mistake or issue in this article, please fix it and submit a pull request on Github (must be signed in to your GitHub account).

I offer a bounty of one coffee, beer, or tea for each pull request that gets merged in. :) Make sure to cc @nathanleclaire in the PR.