Skip to content

Commit e92fa68

Browse files
authored
Merge pull request #654 from abronan/mitigate-stuck-dial-on-initiator-stop
Fix stuck call to Dial when calling Stop on the Initiator
2 parents c07597e + 3939268 commit e92fa68

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

dialer.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"github.com/quickfixgo/quickfix/config"
2626
)
2727

28-
func loadDialerConfig(settings *SessionSettings) (dialer proxy.Dialer, err error) {
28+
func loadDialerConfig(settings *SessionSettings) (dialer proxy.ContextDialer, err error) {
2929
stdDialer := &net.Dialer{}
3030
if settings.HasSetting(config.SocketTimeout) {
3131
timeout, err := settings.DurationSetting(config.SocketTimeout)
@@ -73,9 +73,23 @@ func loadDialerConfig(settings *SessionSettings) (dialer proxy.Dialer, err error
7373
}
7474
}
7575

76-
dialer, err = proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), proxyAuth, dialer)
76+
var proxyDialer proxy.Dialer
77+
78+
proxyDialer, err = proxy.SOCKS5("tcp", fmt.Sprintf("%s:%d", proxyHost, proxyPort), proxyAuth, stdDialer)
79+
if err != nil {
80+
return
81+
}
82+
83+
if contextDialer, ok := proxyDialer.(proxy.ContextDialer); ok {
84+
dialer = contextDialer
85+
} else {
86+
err = fmt.Errorf("proxy does not support context dialer")
87+
return
88+
}
89+
7790
default:
7891
err = fmt.Errorf("unsupported proxy type %s", proxyType)
7992
}
93+
8094
return
8195
}

initiator.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package quickfix
1717

1818
import (
1919
"bufio"
20+
"context"
2021
"crypto/tls"
2122
"strings"
2223
"sync"
@@ -50,7 +51,7 @@ func (i *Initiator) Start() (err error) {
5051
return
5152
}
5253

53-
var dialer proxy.Dialer
54+
var dialer proxy.ContextDialer
5455
if dialer, err = loadDialerConfig(settings); err != nil {
5556
return
5657
}
@@ -142,7 +143,7 @@ func (i *Initiator) waitForReconnectInterval(reconnectInterval time.Duration) bo
142143
return true
143144
}
144145

145-
func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, dialer proxy.Dialer) {
146+
func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, dialer proxy.ContextDialer) {
146147
var wg sync.WaitGroup
147148
wg.Add(1)
148149
go func() {
@@ -162,14 +163,27 @@ func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, di
162163
return
163164
}
164165

166+
ctx, cancel := context.WithCancel(context.Background())
167+
168+
// We start a goroutine in order to be able to cancel the dialer mid-connection
169+
// on receiving a stop signal to stop the initiator.
170+
go func() {
171+
select {
172+
case <-i.stopChan:
173+
cancel()
174+
case <-ctx.Done():
175+
return
176+
}
177+
}()
178+
165179
var disconnected chan interface{}
166180
var msgIn chan fixIn
167181
var msgOut chan []byte
168182

169183
address := session.SocketConnectAddress[connectionAttempt%len(session.SocketConnectAddress)]
170184
session.log.OnEventf("Connecting to: %v", address)
171185

172-
netConn, err := dialer.Dial("tcp", address)
186+
netConn, err := dialer.DialContext(ctx, "tcp", address)
173187
if err != nil {
174188
session.log.OnEventf("Failed to connect: %v", err)
175189
goto reconnect
@@ -208,13 +222,19 @@ func (i *Initiator) handleConnection(session *session, tlsConfig *tls.Config, di
208222
close(disconnected)
209223
}()
210224

225+
// This ensures we properly cleanup the goroutine and context used for
226+
// dial cancelation after successful connection.
227+
cancel()
228+
211229
select {
212230
case <-disconnected:
213231
case <-i.stopChan:
214232
return
215233
}
216234

217235
reconnect:
236+
cancel()
237+
218238
connectionAttempt++
219239
session.log.OnEventf("Reconnecting in %v", session.ReconnectInterval)
220240
if !i.waitForReconnectInterval(session.ReconnectInterval) {

0 commit comments

Comments
 (0)