Skip to content

Commit 80bc3b3

Browse files
authored
Merge pull request #917 from apernet/fix-reconnect
fix: incorrect reconnect logic that causes blocking when dialing connections
2 parents e648321 + ae402d9 commit 80bc3b3

File tree

1 file changed

+32
-29
lines changed

1 file changed

+32
-29
lines changed

core/client/reconnect.go

+32-29
Original file line numberDiff line numberDiff line change
@@ -56,53 +56,56 @@ func (rc *reconnectableClientImpl) reconnect() error {
5656
}
5757
}
5858

59-
func (rc *reconnectableClientImpl) TCP(addr string) (net.Conn, error) {
59+
// clientDo calls f with the current client.
60+
// If the client is nil, it will first reconnect.
61+
// It will also detect if the client is closed, and if so,
62+
// set it to nil for reconnect next time.
63+
func (rc *reconnectableClientImpl) clientDo(f func(Client) (interface{}, error)) (interface{}, error) {
6064
rc.m.Lock()
61-
defer rc.m.Unlock()
6265
if rc.closed {
66+
rc.m.Unlock()
6367
return nil, coreErrs.ClosedError{}
6468
}
6569
if rc.client == nil {
6670
// No active connection, connect first
6771
if err := rc.reconnect(); err != nil {
72+
rc.m.Unlock()
6873
return nil, err
6974
}
7075
}
71-
conn, err := rc.client.TCP(addr)
76+
client := rc.client
77+
rc.m.Unlock()
78+
79+
ret, err := f(client)
7280
if _, ok := err.(coreErrs.ClosedError); ok {
73-
// Connection closed, reconnect
74-
if err := rc.reconnect(); err != nil {
75-
return nil, err
81+
// Connection closed, set client to nil for reconnect next time
82+
rc.m.Lock()
83+
if rc.client == client {
84+
// This check is in case the client is already changed by another goroutine
85+
rc.client = nil
7686
}
77-
return rc.client.TCP(addr)
87+
rc.m.Unlock()
88+
}
89+
return ret, err
90+
}
91+
92+
func (rc *reconnectableClientImpl) TCP(addr string) (net.Conn, error) {
93+
if c, err := rc.clientDo(func(client Client) (interface{}, error) {
94+
return client.TCP(addr)
95+
}); err != nil {
96+
return nil, err
7897
} else {
79-
// OK or some other temporary error
80-
return conn, err
98+
return c.(net.Conn), nil
8199
}
82100
}
83101

84102
func (rc *reconnectableClientImpl) UDP() (HyUDPConn, error) {
85-
rc.m.Lock()
86-
defer rc.m.Unlock()
87-
if rc.closed {
88-
return nil, coreErrs.ClosedError{}
89-
}
90-
if rc.client == nil {
91-
// No active connection, connect first
92-
if err := rc.reconnect(); err != nil {
93-
return nil, err
94-
}
95-
}
96-
conn, err := rc.client.UDP()
97-
if _, ok := err.(coreErrs.ClosedError); ok {
98-
// Connection closed, reconnect
99-
if err := rc.reconnect(); err != nil {
100-
return nil, err
101-
}
102-
return rc.client.UDP()
103+
if c, err := rc.clientDo(func(client Client) (interface{}, error) {
104+
return client.UDP()
105+
}); err != nil {
106+
return nil, err
103107
} else {
104-
// OK or some other temporary error
105-
return conn, err
108+
return c.(HyUDPConn), nil
106109
}
107110
}
108111

0 commit comments

Comments
 (0)