~emersion/soju

69e9b6a439751a42b7b67ba548de00653b8a38bc — Simon Ser 5 months ago 950198a
Retry on temporary net.Listener failure

Instead of stopping to listen, retry on temporary failure. This
can happen when running out of FDs.

Closes: https://todo.sr.ht/~emersion/soju/183
2 files changed, 37 insertions(+), 0 deletions(-)

M ident.go
M server.go
M ident.go => ident.go +2 -0
@@ 74,6 74,8 @@ func (s *Identd) Delete(remoteAddr, localAddr string) {
}

func (s *Identd) Serve(ln net.Listener) error {
	ln = &retryListener{Listener: ln}

	for {
		conn, err := ln.Accept()
		if err != nil {

M server.go => server.go +35 -0
@@ 93,6 93,36 @@ func (g *int64Gauge) Float64() float64 {
	return float64(g.Value())
}

type retryListener struct {
	net.Listener
	Logger Logger

	delay time.Duration
}

func (ln *retryListener) Accept() (net.Conn, error) {
	for {
		conn, err := ln.Listener.Accept()
		if ne, ok := err.(net.Error); ok && ne.Temporary() {
			if ln.delay == 0 {
				ln.delay = 5 * time.Millisecond
			} else {
				ln.delay *= 2
			}
			if max := 1 * time.Second; ln.delay > max {
				ln.delay = max
			}
			if ln.Logger != nil {
				ln.Logger.Printf("accept error (retrying in %v): %v", ln.delay, err)
			}
			time.Sleep(ln.delay)
		} else {
			ln.delay = 0
			return conn, err
		}
	}
}

type Config struct {
	Hostname        string
	Title           string


@@ 328,6 358,11 @@ func (s *Server) handle(ic ircConn) {
}

func (s *Server) Serve(ln net.Listener) error {
	ln = &retryListener{
		Listener: ln,
		Logger:   &prefixLogger{logger: s.Logger, prefix: fmt.Sprintf("listener %v: ", ln.Addr())},
	}

	s.lock.Lock()
	s.listeners[ln] = struct{}{}
	s.lock.Unlock()