internal/jsonrpc2_v2: initialize async before binding

The lsprpc package needs to wait on the newly bound connection to
perform some tear-down, so it must be safe to call conn.Wait from Bind.

Achieve this by switching async from an init pattern to a constructor.
This means moving async fields to pointers, but since they weren't safe
to use prior to initialization anyway this feels correct.

Change-Id: I4f93980915e0b568fbf2db3cd7d062adf06e4b99
Reviewed-on: https://go-review.googlesource.com/c/tools/+/330929
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rob Findley 2021-06-25 11:49:46 -04:00 • committed by Robert Findley
commit 0c89f898b7
3 changed files with 7 additions and 5 deletions

View file

@ -50,7 +50,7 @@ type Connection struct {
writerBox chan Writer writerBox chan Writer
outgoingBox chan map[ID]chan<- *Response outgoingBox chan map[ID]chan<- *Response
incomingBox chan map[ID]*incoming incomingBox chan map[ID]*incoming
async async async *async
} }
type AsyncCall struct { type AsyncCall struct {
@ -87,6 +87,7 @@ func newConnection(ctx context.Context, rwc io.ReadWriteCloser, binder Binder) (
writerBox: make(chan Writer, 1), writerBox: make(chan Writer, 1),
outgoingBox: make(chan map[ID]chan<- *Response, 1), outgoingBox: make(chan map[ID]chan<- *Response, 1),
incomingBox: make(chan map[ID]*incoming, 1), incomingBox: make(chan map[ID]*incoming, 1),
async: newAsync(),
} }
options, err := binder.Bind(ctx, c) options, err := binder.Bind(ctx, c)
@ -104,7 +105,6 @@ func newConnection(ctx context.Context, rwc io.ReadWriteCloser, binder Binder) (
} }
c.outgoingBox <- make(map[ID]chan<- *Response) c.outgoingBox <- make(map[ID]chan<- *Response)
c.incomingBox <- make(map[ID]*incoming) c.incomingBox <- make(map[ID]*incoming)
c.async.init()
// the goroutines started here will continue until the underlying stream is closed // the goroutines started here will continue until the underlying stream is closed
reader := options.Framer.Reader(rwc) reader := options.Framer.Reader(rwc)
readToQueue := make(chan *incoming) readToQueue := make(chan *incoming)

View file

@ -64,10 +64,12 @@ type async struct {
errBox chan error errBox chan error
} }
func (a *async) init() { func newAsync() *async {
var a async
a.ready = make(chan struct{}) a.ready = make(chan struct{})
a.errBox = make(chan error, 1) a.errBox = make(chan error, 1)
a.errBox <- nil a.errBox <- nil
return &a
} }
func (a *async) done() { func (a *async) done() {

View file

@ -42,7 +42,7 @@ type Dialer interface {
type Server struct { type Server struct {
listener Listener listener Listener
binder Binder binder Binder
async async async *async
} }
// Dial uses the dialer to make a new connection, wraps the returned // Dial uses the dialer to make a new connection, wraps the returned
@ -68,8 +68,8 @@ func Serve(ctx context.Context, listener Listener, binder Binder) (*Server, erro
server := &Server{ server := &Server{
listener: listener, listener: listener,
binder: binder, binder: binder,
async: newAsync(),
} }
server.async.init()
go server.run(ctx) go server.run(ctx)
return server, nil return server, nil
} }