Skip to content

Commit 89349a3

Browse files
authored
Fixing races (#53)
* Fixing races During work with library I encounted couple races For example: panic: runtime error: slice bounds out of range [:1026] with capacity 1024 goroutine 125171 [running]: bufio.(*Reader).Peek(0xc012610de0, 0x2) /usr/local/go/src/bufio/bufio.go:165 +0x16a github.com/gorilla/websocket.(*Conn).read(0xc01210cc60, 0x0?) /go/pkg/mod/github.com/gorilla/[email protected]/conn.go:378 +0x26 github.com/gorilla/websocket.(*Conn).advanceFrame(0xc01210cc60) /go/pkg/mod/github.com/gorilla/[email protected]/conn.go:824 +0x6d github.com/gorilla/websocket.(*Conn).NextReader(0xc01210cc60) /go/pkg/mod/github.com/gorilla/[email protected]/conn.go:1034 +0x13e github.com/gorilla/websocket.(*Conn).ReadMessage(0xc00e924620?) /go/pkg/mod/github.com/gorilla/[email protected]/conn.go:1120 +0x13 github.com/aopoltorzhicky/go_kraken/websocket.(*Kraken).listenSocket(0xc0003ba0e0) /go/pkg/mod/github.com/aopoltorzhicky/[email protected]/websocket/kraken.go:198 +0x168 created by github.com/aopoltorzhicky/go_kraken/websocket.(*Kraken).managerThread in goroutine 82 /go/pkg/mod/github.com/aopoltorzhicky/[email protected]/websocket/kraken.go:109 +0x227 So following patch should addres this problem. Issues: 1. Multiple listenSocket methods 2. Possible lock leaks 3. Shouldn't be sends in parallel 4. Switch to close, multiple listeners Refactor * only one close
1 parent 314d408 commit 89349a3

File tree

1 file changed

+33
-39
lines changed

1 file changed

+33
-39
lines changed

websocket/kraken.go

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,20 @@ type Kraken struct {
2424
readTimeout time.Duration
2525
heartbeatTimeout time.Duration
2626

27-
msg chan Update
28-
connect chan struct{}
29-
stop chan struct{}
27+
msg chan Update
28+
stop chan struct{}
3029

31-
wg sync.WaitGroup
30+
lock sync.RWMutex
3231
}
3332

34-
// New -
33+
// NewKraken -
3534
func NewKraken(url string, opts ...KrakenOption) *Kraken {
3635
kraken := Kraken{
3736
url: url,
3837
reconnectTimeout: 5 * time.Second,
3938
readTimeout: 15 * time.Second,
4039
heartbeatTimeout: 10 * time.Second,
4140
subscriptions: make(map[int64]*SubscriptionStatus),
42-
connect: make(chan struct{}, 1),
4341
msg: make(chan Update, 1024),
4442
stop: make(chan struct{}, 1),
4543
}
@@ -53,15 +51,10 @@ func NewKraken(url string, opts ...KrakenOption) *Kraken {
5351

5452
// Connect to the Kraken API, this should only be called once.
5553
func (k *Kraken) Connect() error {
56-
k.wg.Add(1)
57-
go k.managerThread()
58-
5954
if err := k.dial(); err != nil {
6055
return err
6156
}
62-
63-
k.wg.Add(1)
64-
go k.listenSocket()
57+
go k.managerThread()
6558

6659
return nil
6760
}
@@ -85,38 +78,43 @@ func (k *Kraken) dial() error {
8578
}
8679

8780
func (k *Kraken) managerThread() {
88-
defer k.wg.Done()
89-
9081
heartbeat := time.NewTicker(k.heartbeatTimeout)
9182
defer heartbeat.Stop()
9283

84+
connect := make(chan struct{})
85+
stopListener := make(chan struct{})
86+
reconnectCh := make(chan struct{})
87+
go k.listenSocket(stopListener, reconnectCh)
88+
9389
for {
9490
select {
95-
case <-k.stop:
96-
return
97-
case <-k.connect:
91+
case <-connect:
9892
time.Sleep(k.reconnectTimeout)
9993

100-
log.Warnf("reconnecting...")
101-
10294
if err := k.dial(); err != nil {
10395
log.Error(err)
104-
k.connect <- struct{}{}
96+
connect <- struct{}{}
10597
continue
10698
}
10799

108-
k.wg.Add(1)
109-
go k.listenSocket()
110-
111100
if err := k.resubscribe(); err != nil {
112101
log.Error(err)
113102
}
103+
104+
stopListener = make(chan struct{})
105+
reconnectCh = make(chan struct{})
106+
go k.listenSocket(stopListener, reconnectCh)
107+
case <-reconnectCh:
108+
connect <- struct{}{}
109+
case <-k.stop:
110+
return
114111
case <-heartbeat.C:
115112
if err := k.send(PingRequest{
116113
Event: EventPing,
117114
}); err != nil {
118115
log.Println(err)
119-
k.connect <- struct{}{}
116+
close(stopListener)
117+
connect <- struct{}{}
120118
}
121119
}
122120
}
@@ -149,11 +147,6 @@ func (k *Kraken) Listen() <-chan Update {
149147

150148
// Close - provides an interface for a user initiated shutdown.
151149
func (k *Kraken) Close() error {
152-
for i := 0; i < 2; i++ {
153-
k.stop <- struct{}{}
154-
}
155-
k.wg.Wait()
156-
157150
if k.conn != nil {
158151
if err := k.conn.Close(); err != nil {
159152
return err
@@ -162,11 +155,12 @@ func (k *Kraken) Close() error {
162155

163156
close(k.stop)
164157
close(k.msg)
165-
close(k.connect)
166158
return nil
167159
}
168160

169161
func (k *Kraken) send(msg interface{}) error {
162+
k.lock.Lock()
163+
defer k.lock.Unlock()
170164
if k.conn == nil {
171165
return nil
172166
}
@@ -178,14 +172,14 @@ func (k *Kraken) send(msg interface{}) error {
178172
return k.conn.WriteMessage(websocket.TextMessage, data)
179173
}
180174

181-
func (k *Kraken) listenSocket() {
182-
defer k.wg.Done()
183-
184-
if k.conn == nil {
175+
func (k *Kraken) listenSocket(stop chan struct{}, reconnectCh chan struct{}) {
176+
defer close(reconnectCh)
177+
conn := k.conn
178+
if conn == nil {
185179
return
186180
}
187181

188-
if err := k.conn.SetReadDeadline(time.Now().Add(k.readTimeout)); err != nil {
182+
if err := conn.SetReadDeadline(time.Now().Add(k.readTimeout)); err != nil {
189183
log.Error(err)
190184
return
191185
}
@@ -194,17 +188,17 @@ func (k *Kraken) listenSocket() {
194188
select {
195189
case <-k.stop:
196190
return
191+
case <-stop:
192+
return
197193
default:
198-
_, msg, err := k.conn.ReadMessage()
194+
_, msg, err := conn.ReadMessage()
199195
if err != nil {
200196
log.Error(err)
201-
k.connect <- struct{}{}
202197
return
203198
}
204199

205-
if err := k.conn.SetReadDeadline(time.Now().Add(k.readTimeout)); err != nil {
200+
if err := conn.SetReadDeadline(time.Now().Add(k.readTimeout)); err != nil {
206201
log.Error(err)
207-
k.connect <- struct{}{}
208202
return
209203
}
210204

0 commit comments

Comments
 (0)