Thanks for the reply.
I think that's inherently racy. What if the Listen returns an error
just after you've set the closing flag but before you've called Close?
I *think* it's possible to reliably detect this - the only reason
that Accept returns EINVAL instead of errClosing when called on
a closed listener is that the test in Accept TCP does this:
if l == nil || l.fd == nil || l.fd.sysfd < 0 {
return nil, syscall.EINVAL
}
rather than this:
if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
if l.fd.sysfd < 0 {
return nil, errClosing
}
AFAICS the only way that sysfd can become negative is because the connection
was closed, and the way that fds are handled, I don't think a system fd is ever
closed while there's a syscall in progress on it, so we'll never see the
underlying OS error for an operation on a closed fd.
I agree that changing this would lead to an observable difference in behaviour
between Go1.1 and Go1. It's your call as to whether it would be worth changing.
For the record, this is the 99% solution we adopted eventually:
type Server struct {
l net.Listener
errc chan error // with buffer size of 1.
}
func (s *Server) run() {
for {
conn, err := s.l.Accept()
if err != nil {
s.errc <- err
return
}
go s.serve(conn)
}
panic("not reached")
}
func (s *Server) serve(net.Conn) {
}
func (s *Server) Close() (err error) {
// Check whether the listener has returned an error already.
select {
case err = <-s.errc:
default:
}
s.l.Close()
return
}