jsonrpc2: initialize Connection.async before calling binder.Bind
If Connection.async is not initialized before calling binder.Bind()
and a goroutine is spawned in binder.Bind() to monitor the end of the
connection using conn.Wait(), this conn.Wait() can wait forever:
func (s *MyServer) Bind(
ctx context.Context,
conn *jsonrpc2.Connection,
) (jsonrpc2.ConnectionOptions, error) {
// XXX register conn in *MyServer
// monitor the end of the connection
go func() {
conn.Wait()
conn.Close()
// YYY do some cleanup in *MyServer
}()
return jsonrpc2.ConnectionOptions{}, nil
}
The "// YYY do some cleanup in *MyServer" should be reached for each
end of connection.
If Connection.async is initialized too late, after conn.Wait() is
called in the Bind() spawned goroutine, conn.Wait() never returns as
it reads on a nil conn.async.ready channel.
Fixes #56131.
Change-Id: I3c6e69d7dcc860479c4518ebd18e2431606322bc
GitHub-Last-Rev: a7fce452df55a8dd113790d00097c37db454eb3b
GitHub-Pull-Request: golang/exp#45
Reviewed-on: https://go-review.googlesource.com/c/exp/+/436888
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Joedian Reid <joedian@golang.org>
diff --git a/jsonrpc2/conn.go b/jsonrpc2/conn.go
index 2054ab3..882d15b 100644
--- a/jsonrpc2/conn.go
+++ b/jsonrpc2/conn.go
@@ -85,6 +85,7 @@
outgoingBox: make(chan map[ID]chan<- *Response, 1),
incomingBox: make(chan map[ID]*incoming, 1),
}
+ c.async.init()
options, err := binder.Bind(ctx, c)
if err != nil {
@@ -101,7 +102,6 @@
}
c.outgoingBox <- make(map[ID]chan<- *Response)
c.incomingBox <- make(map[ID]*incoming)
- c.async.init()
// the goroutines started here will continue until the underlying stream is closed
reader := options.Framer.Reader(rwc)
readToQueue := make(chan *incoming)