Closed
Description
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (go version
)?
go version go1.10.1 linux/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/taco/.cache/go-build"
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/taco/go"
GORACE=""
GOROOT="/usr/lib/go"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build151412356=/tmp/go-build -gno-record-gcc-switches"
What did you do?
When I run the following code, the listener is closed twice:
package main
import (
"context"
"fmt"
"net"
"net/http"
"time"
)
type Listener struct {
net.Listener
}
func (l *Listener) Close() error {
fmt.Println("close")
return l.Listener.Close()
}
func main() {
ln, _ := net.Listen("tcp", "localhost:9999")
listener := &Listener{ln}
server := &http.Server{}
go func() {
err := server.Serve(listener)
fmt.Println("Serve:", err)
}()
time.Sleep(time.Second) // make sure it starts Serving
server.Shutdown(context.Background())
time.Sleep(time.Second) // make sure to exit Serve and print error before exit
}
The problem appears to be in http/server.go
, where Shutdown
will call srv.closeListenersLocked
which closes our listener. But as Serve
exits, the first row in that function defines a defer l.Close()
that closes the listener as well! I think the latter should go, if I understand it correctly.
What did you expect to see?
I expected the listener to be closed only once, this is causing double closes on underlying structures in our production.
What did you see instead?
You will notice that Close
is called twice.