From d5636108e659ea6ba177aa347ffec008bb10fe3e Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 8 Oct 2026 15:22:43 +0200 Subject: [PATCH] [fix] race condition when trying to reconnect (#5547) --- client/service.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/client/service.go b/client/service.go index 7964050f..c18e94dd 100644 --- a/client/service.go +++ b/client/service.go @@ -275,7 +275,15 @@ func (svr *Service) Run(ctx context.Context) error { } func (svr *Service) keepControllerWorking() { - <-svr.ctl.Done() + // ctl is swapped under ctlMu in loopLoginUntilSuccess / stop; take a + // snapshot before waiting so stop's nil write does not race this read. + svr.ctlMu.RLock() + ctl := svr.ctl + svr.ctlMu.RUnlock() + if ctl == nil { + return + } + <-ctl.Done() // There is a situation where the login is successful but due to certain reasons, // the control immediately exits. It is necessary to limit the frequency of reconnection in this case. @@ -285,8 +293,11 @@ func (svr *Service) keepControllerWorking() { // loopLoginUntilSuccess is another layer of loop that will continuously attempt to // login to the server until successful. svr.loopLoginUntilSuccess(20*time.Second, false) - if svr.ctl != nil { - <-svr.ctl.Done() + svr.ctlMu.RLock() + ctl := svr.ctl + svr.ctlMu.RUnlock() + if ctl != nil { + <-ctl.Done() return false, errors.New("control is closed and try another loop") } // If the control is nil, it means that the login failed and the service is also closed. @@ -377,7 +388,7 @@ func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorC svr.ctlMu.RUnlock() if ctl != nil { - return svr.ctl.UpdateAllConfigurer(proxyCfgs, visitorCfgs) + return ctl.UpdateAllConfigurer(proxyCfgs, visitorCfgs) } return nil }