How hard can it be to clean stuff up in Go?

We have all seen the classic “create a webserver in go” code snippet

package main
import (
"log"
"net/http"
)
func main() {
log.Fatal(http.ListenAndServe(":8080", nil))
}

Very nice, but sooner or later reality kicks in and requires you to do something more complicated. In my case I needed to make sure that I close a connection to a database. Well, I actually didn’t strictly need it, but I was tired of having a lock file from my bolt database lying around, so I assumed this could be done with a simple defer statement.

func main() {
// Do something that requires clean up
defer log.Print("Cleaning up")
log.Fatal(http.ListenAndServe(":8080", nil)
}

… but hitting Ctrl-C didn’t trigger the clean up. My guess was that interrupting the program will somehow make it exit without calling the defer, so I decided to trap the interrupt and let the program terminate normally

func main() {
// Do something that requires clean up
defer log.Print("Cleaning up")
    go log.Fatal(http.ListenAndServe(":8080", nil)
    quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
_ = <-quit
}

And yet nothing happened. It turned out that I successfully caught the interrupt, so I started hunting down the bug. Replacing the “log.Fatal” with a “log.Print” finally triggered my defer, so things doesn’t work as expected when panicing.

I wasn’t completely happy though, since I still want my program to terminate if it, for instance, fails to bind to the port. The “log.Fatal” statement handled that before, but now I need a different approach. To keep things simple I decided to reuse the channel I already made and let the go-routine signal when the program should terminate (if http.ListenAndServe should ever return)

func main() {
// Do something that requires clean up
defer log.Print("Cleaning up")
    quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
    go func() {
log.Print(http.ListenAndServe(“:8080”, nil))
quit <- os.Interrupt
}()
    _ = <-quit
}

Granted … I have only tried this on a Windows machine, so my trouble might be due to weird signal handling on this platform, but in any case, responding to the interrupt and signaling when to terminate a program is a good practice and allows for a lot of flexibility.

Take care out there and remember to clean up after yourself.