Skip to content

Commit 5c8e061

Browse files
authored
Merge pull request #487 from blinklabs-io/fix/panic-send-closed-channel
fix: protect against send on closed channel panics
2 parents 32d9713 + c0c42bf commit 5c8e061

File tree

3 files changed

+29
-21
lines changed

3 files changed

+29
-21
lines changed

connection.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,8 @@ func (c *Connection) shutdown() {
212212
}
213213
// Wait for other goroutines to finish
214214
c.waitGroup.Wait()
215-
// Close channels
215+
// Close consumer error channel to signify connection shutdown
216216
close(c.errorChan)
217-
close(c.protoErrorChan)
218217
// We can only close a channel once, so we have to jump through a few hoops
219218
select {
220219
// The channel is either closed or has an item pending

muxer/muxer.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,6 @@ func (m *Muxer) Stop() {
103103
_ = m.conn.Close()
104104
// Wait for other goroutines to shutdown
105105
m.waitGroup.Wait()
106-
// Close protocol receive channels
107-
// We rely on the individual mini-protocols to close the sender channel
108-
for _, protocolRoles := range m.protocolReceivers {
109-
for _, recvChan := range protocolRoles {
110-
close(recvChan)
111-
}
112-
}
113106
// Close ErrorChan to signify to consumer that we're shutting down
114107
close(m.errorChan)
115108
})
@@ -161,7 +154,10 @@ func (m *Muxer) RegisterProtocol(
161154
if !ok {
162155
return
163156
}
164-
case msg := <-senderChan:
157+
case msg, ok := <-senderChan:
158+
if !ok {
159+
return
160+
}
165161
if err := m.Send(msg); err != nil {
166162
m.sendError(err)
167163
return
@@ -200,7 +196,15 @@ func (m *Muxer) Send(msg *Segment) error {
200196
// readLoop waits for incoming data on the connection, parses the segment, and passes it to the appropriate
201197
// protocol
202198
func (m *Muxer) readLoop() {
203-
defer m.waitGroup.Done()
199+
defer func() {
200+
m.waitGroup.Done()
201+
// Close receiver channels
202+
for _, protocolRoles := range m.protocolReceivers {
203+
for _, recvChan := range protocolRoles {
204+
close(recvChan)
205+
}
206+
}
207+
}()
204208
started := false
205209
for {
206210
// Break out of read loop if we're shutting down

protocol/protocol.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ func (p *Protocol) Start() {
131131
<-p.doneChan
132132
// Wait for all other goroutines to finish
133133
p.waitGroup.Wait()
134-
// Close channels
135-
close(p.sendQueueChan)
136-
close(p.sendStateQueueChan)
137-
close(p.recvReadyChan)
138-
close(p.sendReadyChan)
139134
// Cancel any timer
140135
if p.stateTransitionTimer != nil {
141136
p.stateTransitionTimer.Stop()
@@ -174,20 +169,30 @@ func (p *Protocol) SendMessage(msg Message) error {
174169

175170
// SendError sends an error to the handler in the Ouroboros object
176171
func (p *Protocol) SendError(err error) {
177-
p.config.ErrorChan <- err
172+
select {
173+
case p.config.ErrorChan <- err:
174+
default:
175+
// Discard error if the buffer is full
176+
// The connection will get closed on the first error, so any
177+
// additional errors are unnecessary
178+
return
179+
}
178180
}
179181

180182
func (p *Protocol) sendLoop() {
181-
defer p.waitGroup.Done()
183+
defer func() {
184+
p.waitGroup.Done()
185+
// Close muxer send channel
186+
// We are responsible for closing this channel as the sender, even through it
187+
// was created by the muxer
188+
close(p.muxerSendChan)
189+
}()
182190
var setNewState bool
183191
var newState State
184192
var err error
185193
for {
186194
select {
187195
case <-p.doneChan:
188-
// We are responsible for closing this channel as the sender, even through it
189-
// was created by the muxer
190-
close(p.muxerSendChan)
191196
// Break out of send loop if we're shutting down
192197
return
193198
case <-p.sendReadyChan:

0 commit comments

Comments
 (0)