Skip to content

Commit 58711b2

Browse files
Fix test flake (#4347)
1 parent 879d7a9 commit 58711b2

File tree

5 files changed

+39
-8
lines changed

5 files changed

+39
-8
lines changed

.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,11 @@ linters:
328328
# This is safe (we aren't traversing filesystem boundaries).
329329
path: private/pkg/storage/storageos/bucket.go
330330
text: os.Rename
331+
- linters:
332+
- containedctx
333+
# connCtx is cancelled when the connection is done; the context lifetime is
334+
# tied to the struct, not passed per-call.
335+
path: private/buf/buflsp/buflsp.go
331336
- linters:
332337
- containedctx
333338
# we actually want to embed a context here

private/buf/buflsp/buflsp.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ func Serve(
6161
logger.Info("starting LSP server")
6262

6363
conn := jsonrpc2.NewConn(stream)
64+
// connCtx is a context scoped to the connection's lifetime. It is cancelled
65+
// when the connection is done (or when ctx is cancelled), so that background
66+
// goroutines (e.g. RunChecks) do not outlive the connection.
67+
connCtx, connCancel := context.WithCancel(context.Background())
68+
go func() {
69+
select {
70+
case <-ctx.Done():
71+
case <-conn.Done():
72+
}
73+
connCancel()
74+
}()
6475
lsp := &lsp{
6576
conn: conn,
6677
client: protocol.ClientDispatcher(
@@ -76,6 +87,8 @@ func Serve(
7687
queryExecutor: queryExecutor,
7788
opener: source.NewMap(nil),
7889
irSession: new(ir.Session),
90+
connCtx: connCtx,
91+
connCancel: connCancel,
7992
}
8093
lsp.fileManager = newFileManager(lsp)
8194
lsp.workspaceManager = newWorkspaceManager(lsp)
@@ -100,9 +113,11 @@ func Serve(
100113
// Its handler methods are not defined in buflsp.go; they are defined in other files, grouped
101114
// according to the groupings in
102115
type lsp struct {
103-
conn jsonrpc2.Conn
104-
client protocol.Client
105-
container appext.Container
116+
conn jsonrpc2.Conn
117+
client protocol.Client
118+
container appext.Container
119+
connCtx context.Context // cancelled when the connection is done
120+
connCancel context.CancelFunc // cancels connCtx
106121

107122
logger *slog.Logger
108123
bufVersion string // buf version, set at server creation
@@ -150,7 +165,10 @@ func (l *lsp) newHandler() (jsonrpc2.Handler, error) {
150165
return nil, err
151166
}
152167
actual := protocol.ServerHandler(server, nil)
153-
return jsonrpc2.AsyncHandler(func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
168+
// [protocol.CancelHandler] intercepts $/cancelRequest notifications from the client and
169+
// cancels the context of the matching in-flight request. It must wrap AsyncHandler so
170+
// that the cancellable context is the one running inside each spawned goroutine.
171+
return protocol.CancelHandler(jsonrpc2.AsyncHandler(func(ctx context.Context, reply jsonrpc2.Replier, req jsonrpc2.Request) error {
154172
l.logger.Debug(
155173
"handling request",
156174
slog.String("method", req.Method()),
@@ -181,5 +199,5 @@ func (l *lsp) newHandler() (jsonrpc2.Handler, error) {
181199
)
182200
}
183201
return nil
184-
}), nil
202+
})), nil
185203
}

private/buf/buflsp/file.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ func (f *file) RunChecks(ctx context.Context) {
10621062
}
10631063

10641064
const checkTimeout = 30 * time.Second
1065-
ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), checkTimeout)
1065+
ctx, cancel := context.WithTimeout(f.lsp.connCtx, checkTimeout)
10661066
f.cancelChecks = cancel
10671067

10681068
go func() {
@@ -1079,7 +1079,7 @@ func (f *file) RunChecks(ctx context.Context) {
10791079
); err != nil {
10801080
var fileAnnotationSet bufanalysis.FileAnnotationSet
10811081
if !errors.As(err, &fileAnnotationSet) {
1082-
if errors.Is(err, context.Canceled) {
1082+
if errors.Is(err, context.Canceled) || ctx.Err() != nil {
10831083
f.lsp.logger.DebugContext(ctx, "checks cancelled", slog.String("uri", f.uri.Filename()), xslog.ErrorAttr(err))
10841084
} else if errors.Is(err, context.DeadlineExceeded) {
10851085
f.lsp.logger.WarnContext(ctx, "checks deadline exceeded", slog.String("uri", f.uri.Filename()), xslog.ErrorAttr(err))

private/buf/buflsp/image.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,9 @@ func buildImage(
8585
var diagnostics []protocol.Diagnostic
8686
compiled, err := compiler.Compile(ctx, path)
8787
if err != nil {
88-
logger.Warn("error building image", slog.String("path", path), xslog.ErrorAttr(err))
88+
if !errors.Is(err, context.Canceled) {
89+
logger.WarnContext(ctx, "error building image", slog.String("path", path), xslog.ErrorAttr(err))
90+
}
8991
var errorWithPos reporter.ErrorWithPos
9092
if errors.As(err, &errorWithPos) {
9193
diagnostics = []protocol.Diagnostic{newDiagnostic(errorWithPos, false, opener, logger)}

private/buf/buflsp/server.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,12 @@ func (s *server) SetTrace(
193193
// The client will wait until Shutdown returns, and then call Exit.
194194
func (s *server) Shutdown(ctx context.Context) error {
195195
s.lsp.shutdown = true
196+
// Cancel any in-progress checks for all tracked files, then cancel the
197+
// connection context to stop any remaining background goroutines.
198+
for _, file := range s.lsp.fileManager.uriToFile.Range {
199+
file.CancelChecks(ctx)
200+
}
201+
s.lsp.connCancel()
196202
return nil
197203
}
198204

0 commit comments

Comments
 (0)