Skip to content

Commit 0e222f5

Browse files
Bryan C. Millsgopherbot
Bryan C. Mills
authored andcommitted
internal/jsonrpc2_v2: close the underlying connection if Wait is called instead of Close
(*Server).run calls Wait on all of its connections before returning, but does not call Close explicitly. If Close is necessary to release resources (as is often the case for a net.Conn), the server ends up leaking resources. For golang/go#46047 Change-Id: I4ad048c4f5e5d3f14f992eee6388acd42398c26b Reviewed-on: https://go-review.googlesource.com/c/tools/+/388599 TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Run-TryBot: Bryan Mills <[email protected]> Auto-Submit: Bryan Mills <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent bc4e384 commit 0e222f5

File tree

1 file changed

+32
-11
lines changed

1 file changed

+32
-11
lines changed

internal/jsonrpc2_v2/conn.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/json"
1010
"fmt"
1111
"io"
12+
"sync"
1213
"sync/atomic"
1314

1415
"golang.org/x/tools/internal/event"
@@ -45,8 +46,11 @@ type ConnectionOptions struct {
4546
// Connection is bidirectional; it does not have a designated server or client
4647
// end.
4748
type Connection struct {
48-
seq int64 // must only be accessed using atomic operations
49-
closer io.Closer
49+
seq int64 // must only be accessed using atomic operations
50+
51+
closeOnce sync.Once
52+
closer io.Closer
53+
5054
writerBox chan Writer
5155
outgoingBox chan map[ID]chan<- *Response
5256
incomingBox chan map[ID]*incoming
@@ -275,14 +279,13 @@ func (c *Connection) Wait() error {
275279
// This does not cancel in flight requests, but waits for them to gracefully complete.
276280
func (c *Connection) Close() error {
277281
// close the underlying stream
278-
if err := c.closer.Close(); err != nil && !isClosingError(err) {
279-
return err
280-
}
282+
c.closeOnce.Do(func() {
283+
if err := c.closer.Close(); err != nil {
284+
c.async.setError(err)
285+
}
286+
})
281287
// and then wait for it to cause the connection to close
282-
if err := c.Wait(); err != nil && !isClosingError(err) {
283-
return err
284-
}
285-
return nil
288+
return c.Wait()
286289
}
287290

288291
// readIncoming collects inbound messages from the reader and delivers them, either responding
@@ -295,7 +298,9 @@ func (c *Connection) readIncoming(ctx context.Context, reader Reader, toQueue ch
295298
msg, n, err := reader.Read(ctx)
296299
if err != nil {
297300
// The stream failed, we cannot continue
298-
c.async.setError(err)
301+
if !isClosingError(err) {
302+
c.async.setError(err)
303+
}
299304
return
300305
}
301306
switch msg := msg.(type) {
@@ -395,7 +400,23 @@ func (c *Connection) manageQueue(ctx context.Context, preempter Preempter, fromR
395400
}
396401

397402
func (c *Connection) deliverMessages(ctx context.Context, handler Handler, fromQueue <-chan *incoming) {
398-
defer c.async.done()
403+
defer func() {
404+
// Close the underlying ReadWriteCloser if not already closed. We're about
405+
// to mark the Connection as done, so we'd better actually be done! 😅
406+
//
407+
// TODO(bcmills): This is actually a bit premature, since we may have
408+
// asynchronous handlers still in flight at this point, but it's at least no
409+
// more premature than calling c.async.done at this point (which we were
410+
// already doing). This will get a proper fix in https://go.dev/cl/388134.
411+
c.closeOnce.Do(func() {
412+
if err := c.closer.Close(); err != nil {
413+
c.async.setError(err)
414+
}
415+
})
416+
417+
c.async.done()
418+
}()
419+
399420
for entry := range fromQueue {
400421
// cancel any messages in the queue that we have a pending cancel for
401422
var result interface{}

0 commit comments

Comments
 (0)