Skip to content

Commit

Permalink
server: fix Serve() vs. immediate Shutdown() race.
Browse files Browse the repository at this point in the history
Fix a race where an asynchronous server.Serve() invoked in a
a goroutine races with an almost immediate server.Shutdown().
If Shutdown() finishes its locked closing of listeners before
Serve() gets around to add the new one, Serve will sit stuck
forever in l.Accept(), unless the caller closes the listener
in addition to Shutdown().

This is probably almost impossible to trigger in real life,
but some of the unit tests, which run the server and client
in the same process, occasionally do trigger this. Then, if
the test tries to verify a final ErrServerClosed error from
Serve() after Shutdown() it gets stuck forever.

Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
  • Loading branch information
klihub committed Sep 13, 2024
1 parent 1bb3923 commit 5ee295b
Showing 1 changed file with 14 additions and 1 deletion.
15 changes: 14 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,18 @@ func (s *Server) RegisterService(name string, desc *ServiceDesc) {
}

func (s *Server) Serve(ctx context.Context, l net.Listener) error {
s.addListener(l)
s.mu.Lock()
s.addListenerLocked(l)
defer s.closeListener(l)

select {
case <-s.done:
s.mu.Unlock()
return ErrServerClosed
default:
}
s.mu.Unlock()

var (
backoff time.Duration
handshaker = s.config.handshaker
Expand Down Expand Up @@ -191,6 +200,10 @@ func (s *Server) Close() error {
func (s *Server) addListener(l net.Listener) {
s.mu.Lock()
defer s.mu.Unlock()
s.addListenerLocked(l)
}

func (s *Server) addListenerLocked(l net.Listener) {
s.listeners[l] = struct{}{}
}

Expand Down

0 comments on commit 5ee295b

Please sign in to comment.