Merge pull request #917 from apernet/fix-reconnect

fix: incorrect reconnect logic that causes blocking when dialing connections
This commit is contained in:
Toby 2024-01-26 18:56:07 -08:00 committed by GitHub
commit 80bc3b3a44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -56,53 +56,56 @@ func (rc *reconnectableClientImpl) reconnect() error {
} }
} }
func (rc *reconnectableClientImpl) TCP(addr string) (net.Conn, error) { // clientDo calls f with the current client.
// If the client is nil, it will first reconnect.
// It will also detect if the client is closed, and if so,
// set it to nil for reconnect next time.
func (rc *reconnectableClientImpl) clientDo(f func(Client) (interface{}, error)) (interface{}, error) {
rc.m.Lock() rc.m.Lock()
defer rc.m.Unlock()
if rc.closed { if rc.closed {
rc.m.Unlock()
return nil, coreErrs.ClosedError{} return nil, coreErrs.ClosedError{}
} }
if rc.client == nil { if rc.client == nil {
// No active connection, connect first // No active connection, connect first
if err := rc.reconnect(); err != nil { if err := rc.reconnect(); err != nil {
rc.m.Unlock()
return nil, err return nil, err
} }
} }
conn, err := rc.client.TCP(addr) client := rc.client
rc.m.Unlock()
ret, err := f(client)
if _, ok := err.(coreErrs.ClosedError); ok { if _, ok := err.(coreErrs.ClosedError); ok {
// Connection closed, reconnect // Connection closed, set client to nil for reconnect next time
if err := rc.reconnect(); err != nil { rc.m.Lock()
return nil, err if rc.client == client {
// This check is in case the client is already changed by another goroutine
rc.client = nil
} }
return rc.client.TCP(addr) rc.m.Unlock()
}
return ret, err
}
func (rc *reconnectableClientImpl) TCP(addr string) (net.Conn, error) {
if c, err := rc.clientDo(func(client Client) (interface{}, error) {
return client.TCP(addr)
}); err != nil {
return nil, err
} else { } else {
// OK or some other temporary error return c.(net.Conn), nil
return conn, err
} }
} }
func (rc *reconnectableClientImpl) UDP() (HyUDPConn, error) { func (rc *reconnectableClientImpl) UDP() (HyUDPConn, error) {
rc.m.Lock() if c, err := rc.clientDo(func(client Client) (interface{}, error) {
defer rc.m.Unlock() return client.UDP()
if rc.closed { }); err != nil {
return nil, coreErrs.ClosedError{}
}
if rc.client == nil {
// No active connection, connect first
if err := rc.reconnect(); err != nil {
return nil, err return nil, err
}
}
conn, err := rc.client.UDP()
if _, ok := err.(coreErrs.ClosedError); ok {
// Connection closed, reconnect
if err := rc.reconnect(); err != nil {
return nil, err
}
return rc.client.UDP()
} else { } else {
// OK or some other temporary error return c.(HyUDPConn), nil
return conn, err
} }
} }