Skip to content

Commit 50e1a5c

Browse files
committed
fix(client): prevent premature reconnection due to async timeout flag
Fixes a race condition where an immediate check of the asynchronous serverChecker flag could trigger an unnecessary reconnection loop right after a new transport path is established.
1 parent 6857e9c commit 50e1a5c

1 file changed

Lines changed: 14 additions & 2 deletions

File tree

tsshd/client.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,12 +515,24 @@ func (c *SshUdpClient) tryToReconnect() {
515515
c.debug("new transport path established")
516516
c.reconnectError.Store(nil)
517517

518-
// blocks until the server becomes active or a timeout occurs
519-
for !c.clientProxy.serverChecker.isTimeout() {
518+
// After a successful reconnection, activeChecker.isTimeout() does not immediately become false.
519+
// We wait here until the heartbeat normalizes (activeChecker.isTimeout() == false).
520+
//
521+
// Note: Before renewTransportPath returns successfully, it calls serverChecker.updateNow(),
522+
// so we expect serverChecker.isTimeout() to be false. However, because serverChecker.timeoutFlag
523+
// is updated asynchronously by another goroutine, an immediate check might incorrectly return true.
524+
// To prevent a premature reconnection loop, we wait a short interval before checking serverChecker.isTimeout().
525+
//
526+
// If the connection drops again while waiting (serverChecker.isTimeout() == true),
527+
// we break the loop to trigger another reconnection attempt.
528+
for {
520529
time.Sleep(c.intervalTime)
521530
if !c.activeChecker.isTimeout() {
522531
return
523532
}
533+
if c.clientProxy.serverChecker.isTimeout() {
534+
break
535+
}
524536
}
525537
}
526538
}

0 commit comments

Comments
 (0)