Skip to content

Commit fef8b62

Browse files
committed
gopls/internal/server: fix a (mostly) benign race in diagnostics
In golang/go#65312, we observed a test flake due to what I believe is a mostly benign race in diagnostics: 1. diagnoseSnapshot requests session.Views() 2. diagnoseSnapshot computes diagnostics 3. diagnoseSnapshot calls updateDiagnostics for the views from (1) This means that if a view V were created and diagnosed between (1) and (3), the call to updateDiagnostics will prune the diagnostics for V, because it doesn't think it's an active view. This is fundamentally a flaw in the design of multi-view diagnostics: we don't a priori know which views apply to a given file, so we have to do this complicated join. Nevertheless, I think this race is avoidable by requesting session.Views() inside the critical section of updateDiagnostics. In this way, it's not possible to overwrite diagnostics reported by a different View, without also observing that View. Fixes golang/go#65312 Change-Id: Ie4116fbfac2837f7d142c7865758ff6bac92fce4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/563957 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent df9c1c7 commit fef8b62

File tree

1 file changed

+9
-8
lines changed

1 file changed

+9
-8
lines changed

gopls/internal/server/diagnostics.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ func (s *server) diagnoseSnapshot(snapshot *cache.Snapshot, changedURIs []protoc
178178
ctx, done := event.Start(ctx, "Server.diagnoseSnapshot", snapshot.Labels()...)
179179
defer done()
180180

181-
allViews := s.session.Views()
182181
if delay > 0 {
183182
// 2-phase diagnostics.
184183
//
@@ -207,7 +206,7 @@ func (s *server) diagnoseSnapshot(snapshot *cache.Snapshot, changedURIs []protoc
207206
}
208207
return
209208
}
210-
s.updateDiagnostics(ctx, allViews, snapshot, diagnostics, false)
209+
s.updateDiagnostics(ctx, snapshot, diagnostics, false)
211210
}
212211

213212
if delay < minDelay {
@@ -230,7 +229,7 @@ func (s *server) diagnoseSnapshot(snapshot *cache.Snapshot, changedURIs []protoc
230229
}
231230
return
232231
}
233-
s.updateDiagnostics(ctx, allViews, snapshot, diagnostics, true)
232+
s.updateDiagnostics(ctx, snapshot, diagnostics, true)
234233
}
235234

236235
func (s *server) diagnoseChangedFiles(ctx context.Context, snapshot *cache.Snapshot, uris []protocol.DocumentURI) (diagMap, error) {
@@ -671,10 +670,7 @@ func (s *server) updateCriticalErrorStatus(ctx context.Context, snapshot *cache.
671670

672671
// updateDiagnostics records the result of diagnosing a snapshot, and publishes
673672
// any diagnostics that need to be updated on the client.
674-
//
675-
// The allViews argument should be the current set of views present in the
676-
// session, for the purposes of trimming diagnostics produced by deleted views.
677-
func (s *server) updateDiagnostics(ctx context.Context, allViews []*cache.View, snapshot *cache.Snapshot, diagnostics diagMap, final bool) {
673+
func (s *server) updateDiagnostics(ctx context.Context, snapshot *cache.Snapshot, diagnostics diagMap, final bool) {
678674
ctx, done := event.Start(ctx, "Server.publishDiagnostics")
679675
defer done()
680676

@@ -697,8 +693,13 @@ func (s *server) updateDiagnostics(ctx context.Context, allViews []*cache.View,
697693
return
698694
}
699695

696+
// golang/go#65312: since the set of diagnostics depends on the set of views,
697+
// we get the views *after* locking diagnosticsMu. This ensures that
698+
// updateDiagnostics does not incorrectly delete diagnostics that have been
699+
// set for an existing view that was created between the call to
700+
// s.session.Views() and updateDiagnostics.
700701
viewMap := make(viewSet)
701-
for _, v := range allViews {
702+
for _, v := range s.session.Views() {
702703
viewMap[v] = unit{}
703704
}
704705

0 commit comments

Comments
 (0)