Can you defer a function call before a SIGINT

My main function looks something like this:

func main() {
    dpPool := &DatabasePool{db: connectDb()}
	defer dpPool.db.Close()
	ctx := context.Background()
	dpPool.setUpTables(ctx)
	defer dpPool.dropTables(ctx)
	mux := http.NewServeMux()
	mux.HandleFunc("/", getRoot)
	mux.HandleFunc("/hello", getHello)
	mux.HandleFunc("/ping", ping)

	server := &http.Server{
		Addr:    "<address>",
		Handler: mux,
		BaseContext: func(l net.Listener) context.Context {
			ctx = context.WithValue(ctx, keyServerAddr, l.Addr().String())
			return ctx
		},
	}

	err := server.ListenAndServe()
	if errors.Is(err, http.ErrServerClosed) {
		fmt.Printf("server closed\n")
	} else if err != nil {
		fmt.Printf("error listening for server: %s\n", err)
	}
}

I am trying to write a web server, and I want to close my database connection pool and drop tables when I close the server. I typically do that now with control + C, which SIGINT’s the program. The tables are not dropping so I think it has something to do with defer. Any help would be appreciated. Thank you in advance. If there is a need for more information, please let me know.

EDIT: I found a solution with this Stackoverflow post. It is a messy workaround, but it works:
In Main:

    c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for sig := range c {
			fmt.Println(sig)
			dpPool.dropTables(ctx)
			fmt.Println("Dropped those tables :)")
			dpPool.db.Close()
			pprof.StopCPUProfile()
			os.Exit(1)
		}
	}()

If there is a better way to handle cleanup functions when a SIGINT interrupt occurs, please let me know

Hello there. I use context, created in main and pass it around. As soon as the exit signal was caught I just cancel this context and can make clean-ups in place, but not in main.

1 Like

how do you catch the exit signal, the same way I did, and what do you mean by cancelling context

Yeah, almost like you did. I don’t think there will be any other elegant way. Something like this:

func exitContext(parent context.Context) (context.Context, context.CancelFunc) {
	ctx, cancel := context.WithCancel(parent)

	go func() {
		defer cancel()

		osSignalCh := make(chan os.Signal, 1)
		defer func() {
			signal.Stop(osSignalCh)
			close(osSignalCh)
		}()

		signal.Notify(osSignalCh,
			syscall.SIGINT,
			syscall.SIGTERM,
			os.Interrupt,
		)

		for {
			select {
			case <-ctx.Done():
				return
			case <-osSignalCh:
				return
			}
		}
	}()

	return ctx, cancel
}

I use this function to wrap the context and pass it to other functions. As soon as the signal comes, the context is going to be canceled and I’ll be able to gracefully handle shutdowns in places.

1 Like