From f4a5a581e841b9e61ba31f60e94d42e3ab7598be Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Fri, 13 Sep 2024 15:06:23 +0300 Subject: [PATCH] server: fix Serve() vs. immediate Shutdown() race. 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 --- server.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/server.go b/server.go index 26419831d..bb71de677 100644 --- a/server.go +++ b/server.go @@ -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 @@ -188,9 +197,7 @@ func (s *Server) Close() error { return err } -func (s *Server) addListener(l net.Listener) { - s.mu.Lock() - defer s.mu.Unlock() +func (s *Server) addListenerLocked(l net.Listener) { s.listeners[l] = struct{}{} }