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?
Never use a nil
context. Use either context.Background()
or context.TODO()
.