Handle Ctrl-C (SIGINT) in a Go Program with NotifyContext
Cancel work on Ctrl-C with signal.NotifyContext, which returns a context that ends on SIGINT or SIGTERM. Call stop afterward to restore defaults. Go 1.16 or later.
A long-running Go program should shut down cleanly when it receives an interrupt.
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
go func() {
time.Sleep(200 * time.Millisecond)
syscall.Kill(os.Getpid(), syscall.SIGINT)
}()
select {
case <-ctx.Done():
fmt.Println("interrupted:", ctx.Err())
case <-time.After(5 * time.Second):
fmt.Println("no signal")
}
stop()
fmt.Println("default handling is back; a second Ctrl-C would now kill the process")
}interrupted: context canceled
default handling is back; a second Ctrl-C would now kill the processsignal.NotifyContext(parent, signals...) returns a derived context that is canceled when one of the signals arrives, so the same select on ctx.Done() that handles timeouts also handles Ctrl-C. The program sends itself SIGINT after 200 milliseconds and the output shows interrupted: context canceled. Calling stop() unregisters the handler, and from then on a second signal ends the process in the usual way. See signal.NotifyContext.
syscall.Kill is Unix only; on Windows only os.Interrupt is delivered. The context says that a signal arrived, not which one, so use signal.Notify with a channel when the signal number matters. Always call stop, or the handler stays registered for the life of the process.