Skip to content

Lingering Deadlock when Write and CloseRead called concurrently in 1.8.6 #248

Closed
@RTann

Description

@RTann

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions