From 8d815e365cd2cda2e865dd2e272ebb5a3d4b8772 Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Thu, 20 Jun 2019 08:36:27 +0200
Subject: [PATCH] rpc: fix rare deadlock when canceling HTTP call context
 (#19715)

When cancelling the context for a call on a HTTP-based client while the
call is running, the select in requestOp.wait may hit the <-context.Done()
case instead of the <-op.resp case. This doesn't happen often -- our
cancel test hasn't caught this even though it ran thousands of times
on CI since the RPC client was added.

Fixes #19714
---
 rpc/client.go | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/rpc/client.go b/rpc/client.go
index 02029dc8f..16511747f 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -137,9 +137,11 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, erro
 	select {
 	case <-ctx.Done():
 		// Send the timeout to dispatch so it can remove the request IDs.
-		select {
-		case c.reqTimeout <- op:
-		case <-c.closing:
+		if !c.isHTTP {
+			select {
+			case c.reqTimeout <- op:
+			case <-c.closing:
+			}
 		}
 		return nil, ctx.Err()
 	case resp := <-op.resp: