Graceful Shutdown: handling CTRL+C

August 19, 2018

If you have long-running processes in Go, you often need a way to properly shutdown the application. The common way to do this is to perform some action when the user presses CTRL+C on their keyboard.

In Linux/Unix, doing a CTRL+C sends a signal SIGINT (the interrupt signal) to the process. How do we catch this in a Go application and do something with it?

Go defines the signal.Notify method which can be used for this.

This is the example taken from the Go docs which shows how to use this:

package main

import (
    "fmt"
    "os"
    "os/signal"
)

func main() {

    // Set up channel on which to send signal notifications.
    // We must use a buffered channel or risk missing the signal
    // if we're not ready to receive when the signal is sent.
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    s := <-c

    // The signal is received, you can now do the cleanup
    fmt.Println("Got signal:", s)

}

In most programs like these, you will start the main part of the app in a goroutine (otherwise, they will block the handling of CTRL+C).

For example, it might be that you run an infinite loop:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "time"
)

func main() {

    // Run the main part of the app in a goroutine
    go func() {
        for {
            fmt.Println("Doing something")
            time.Sleep(2 * time.Second)
        }
    }()

    // Set up channel on which to send signal notifications.
    // We must use a buffered channel or risk missing the signal
    // if we're not ready to receive when the signal is sent.
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    s := <-c

    // The signal is received, you can now do the cleanup
    fmt.Println("Got signal:", s)

}

As you can see, once you understand the idea behind channels and goroutines, doing things like these is really simple.