Pergunta

A few months ago I was thinking how to implement a closable event loop in Go, for an RPC library. I managed to facilitate closing the server like so:

type Server struct {
    listener net.Listener
    closeChan chan bool
    routines sync.WaitGroup
}

func (s *Server) Serve() {
    s.routines.Add(1)
    defer s.routines.Done()
    defer s.listener.Close()

    for {
        select {
            case <-s.closeChan:
                // close server etc.
            default:
                s.listener.SetDeadline(time.Now().Add(2 * time.Second))
                conn, _ := s.listener.Accept()
                // handle conn routine
        }
    }
}

func (s *Server) Close() {
    s.closeChan <- true // signal to close serve routine
    s.routines.Wait()
}

The problem that I've found with this implementation is it involves a timeout, which means minimum close time is 2 seconds more than it could be. Is there a more idiomatic method of creating an event loop?

Foi útil?

Solução

I don't think that event loops in Go need to be loops.

It would seem simpler to handle closing and connections in separate goroutines:

go func() {
    <-s.closeChan
    // close server, release resources, etc.
    s.listener.Close()
}()
for {
    conn, err := s.listener.Accept()
    if err != nil {
         // log, return
    }
    // handle conn routine
}

Note that you might also close the listener directly in your Close function without using a channel. What I have done here is used the error return value of Listener.Accept to facilitate inter-routine communication.

If at some point of the closing and connection handling implementations you need to protect some resources you're closing while you're answering, you may use a Mutex. But it's generally possible to avoid that.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top