In the fix for the deadlock introduced in 1.8.5, we have the following:
err = c.writeFrameMu.lock(ctx)
if err != nil {
return 0, err
}
defer c.writeFrameMu.unlock()
// If the state says a close has already been written, we wait until
// the connection is closed and return that error.
//
// However, if the frame being written is a close, that means its the close from
// the state being set so we let it go through.
c.closeMu.Lock()
wroteClose := c.wroteClose
c.closeMu.Unlock()
if wroteClose && opcode != opClose {
select {
case <-ctx.Done():
return 0, ctx.Err()
case <-c.closed:
return 0, c.closeErr
}
}
...
If there is a (*Conn).Write that passes the if condition, then it waits for at least one of two channels to close, while still holding the writeFrameMu lock. Since c.wroteClose is set before (*Conn).CloseRead gets to call (*Conn).writeFrame (see (*Conn).writeClose), it is possible that (*Conn).CloseRead calls (*Conn).writeFrame after (*Conn).Write gets stuck, so (*Conn).CloseRead will get stuck waiting to get writeFrameMu, and no progress can be made.
One option would be to unlock writeFrameMu immediately after passing the if condition. defer c.writeFrameMu.unlock() would need to be removed since the mutex implementation doesn't allow for unlocking twice.
In the fix for the deadlock introduced in 1.8.5, we have the following:
If there is a
(*Conn).Writethat passes theifcondition, then it waits for at least one of two channels to close, while still holding thewriteFrameMulock. Sincec.wroteCloseis set before(*Conn).CloseReadgets to call(*Conn).writeFrame(see(*Conn).writeClose), it is possible that(*Conn).CloseReadcalls(*Conn).writeFrameafter(*Conn).Writegets stuck, so(*Conn).CloseReadwill get stuck waiting to getwriteFrameMu, and no progress can be made.One option would be to unlock
writeFrameMuimmediately after passing theifcondition.defer c.writeFrameMu.unlock()would need to be removed since the mutex implementation doesn't allow for unlocking twice.