Search code examples
gogoroutinepanic

HTTP.Server Shutdown Causes Runtime Error


I have a goroutine written in Go 1.9.2 on darwin/amd64 that causes a runtime error: invalid memory address or nil pointer dereference. I think it's because of a race condition of some kind related to the order of routines exiting, but I'm not sure.

There are several things the main application is doing, so I launched the webserver as a goroutine which then listens for an exit signal from the parent process and tries to shut everything down cleanly before returning.

Here's the function:

// WebServer defines the handler endpoints and launches the web server listener
func WebServer(wg *sync.WaitGroup) {

    // Make sure the exit is noted
    defer wg.Done()

    // Endpoint, handler function
    http.HandleFunc("/version", WebShowVersion)

    // Go forth and serve
    // Define the server struct
    srv := &http.Server{Addr: ":8080"}

    // Now go serve
    chnToLogger <- "Launching web server on port 8080"
    go func() {
        err := srv.ListenAndServe()
        if err != nil &&
            err.Error() != "http: Server closed" {

            // There...was an error
            chnToLoggerError <- "Error launching web server: " + err.Error()
        }
    }()

    // Listen for a shutdown
    for {
        select {
        case <-chnQuitNow:

            // Shut down the process
            chnToLogger <- "Shutting down web server process"
            if err := srv.Shutdown(nil); err != nil {

                // There was a problem shutting down gracefully
                chnToLoggerError <- "Error shutting down web server: " + err.Error()
                return
            }

            // Done
            chnToLogger <- "Web server has shut down now"
            return

        default:
            continue
        }
    }

    // Done
    return
}

The panic states:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x11f7e61]

goroutine 10 [running]:
net/http.(*Server).Shutdown(0xc420158000, 0x0, 0x0, 0x0, 0x0)
    /usr/local/go/src/net/http/server.go:2506 +0x1b1
main.WebServer(0xc4200164a0)
    /Users/me/go/src/testproj/webhandling.go:45 +0x146
created by main.main
    /Users/me/go/src/testproj/main.go:94 +0x3d6

The line 45 in webhandling.go is:

if err := srv.Shutdown(nil); err != nil {

While it's not consistent in being triggered, it does seem to only happen if I launch the program and use a web client to pull localhost:8080/version a few times, leading me to wonder if srv.Shutdown(nil) is trying to close a connection that has a reference but has exited already?

1) Is it possible to get the server to shut down web server processes safely and completely before returning from the goroutine?

2) What is causing the inconsistent runtime error to be triggered?


Solution

  • Never use a nil context. Use either context.Background() or context.TODO().