in connection.go [843:901]
func (c *Connection) checkExchanges() {
c.callOnExchangeChange()
moveState := func(fromState, toState connectionState) bool {
err := c.withStateLock(func() error {
if c.state != fromState {
return errors.New("")
}
c.state = toState
return nil
})
return err == nil
}
curState := c.readState()
origState := curState
if curState != connectionClosed && c.stoppedExchanges.Load() {
if moveState(curState, connectionClosed) {
curState = connectionClosed
}
}
if curState == connectionStartClose {
if !c.relay.canClose() {
return
}
if c.inbound.count() == 0 && moveState(connectionStartClose, connectionInboundClosed) {
curState = connectionInboundClosed
}
}
if curState == connectionInboundClosed {
// Safety check -- this should never happen since we already did the check
// when transitioning to connectionInboundClosed.
if !c.relay.canClose() {
c.relay.logger.Error("Relay can't close even though state is InboundClosed.")
return
}
if c.outbound.count() == 0 && moveState(connectionInboundClosed, connectionClosed) {
curState = connectionClosed
}
}
if curState != origState {
// If the connection is closed, we can notify writeFrames to stop which
// closes the underlying network connection. We never close sendCh to avoid
// races causing panics, see 93ef5c112c8b321367ae52d2bd79396e2e874f31
if curState == connectionClosed {
close(c.stopCh)
}
c.log.WithFields(
LogField{"newState", curState},
).Debug("Connection state updated during shutdown.")
c.callOnCloseStateChange()
}
}