internal/jsonrpc2_v2: fix a potential deadlock when (*Conn).Close is invoked during Bind

This fixes the goroutine leak reported in
https://build.golang.org/log/ae36d36843ca240e9e080886417a8798dd4c9618.

Fixes golang/go#46047 (hopefully for real this time).

Change-Id: I360e54d819849a35284c61d3a0655cc175d81f77
Reviewed-on: https://go-review.googlesource.com/c/tools/+/448095
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/internal/jsonrpc2_v2/conn.go b/internal/jsonrpc2_v2/conn.go
index 085e775..60afa70 100644
--- a/internal/jsonrpc2_v2/conn.go
+++ b/internal/jsonrpc2_v2/conn.go
@@ -82,6 +82,7 @@
 // Connection.
 type inFlightState struct {
 	connClosing bool  // true when the Connection's Close method has been called
+	reading     bool  // true while the readIncoming goroutine is running
 	readErr     error // non-nil when the readIncoming goroutine exits (typically io.EOF)
 	writeErr    error // non-nil if a call to the Writer has failed with a non-canceled Context
 
@@ -140,14 +141,13 @@
 			s.closeErr = s.closer.Close()
 			s.closer = nil // prevent duplicate Close calls
 		}
-		if s.readErr == nil {
+		if s.reading {
 			// The readIncoming goroutine is still running. Our call to Close should
 			// cause it to exit soon, at which point it will make another call to
-			// updateInFlight, set s.readErr to a non-nil error, and mark the
-			// Connection done.
+			// updateInFlight, set s.reading to false, and mark the Connection done.
 		} else {
-			// The readIncoming goroutine has exited. Since everything else is idle,
-			// we're completely done.
+			// The readIncoming goroutine has exited, or never started to begin with.
+			// Since everything else is idle, we're completely done.
 			if c.onDone != nil {
 				c.onDone()
 			}
@@ -240,10 +240,18 @@
 	reader := framer.Reader(rwc)
 
 	c.updateInFlight(func(s *inFlightState) {
+		select {
+		case <-c.done:
+			// Bind already closed the connection; don't start a goroutine to read it.
+			return
+		default:
+		}
+
 		// The goroutine started here will continue until the underlying stream is closed.
 		//
 		// (If the Binder closed the Connection already, this should error out and
 		// return almost immediately.)
+		s.reading = true
 		go c.readIncoming(ctx, reader, options.Preempter)
 	})
 	return c
@@ -514,6 +522,7 @@
 	}
 
 	c.updateInFlight(func(s *inFlightState) {
+		s.reading = false
 		s.readErr = err
 
 		// Retire any outgoing requests that were still in flight: with the Reader no