@@ -29,6 +29,7 @@ import (
29
29
"golang.org/x/tools/gopls/internal/settings"
30
30
"golang.org/x/tools/gopls/internal/util/maps"
31
31
"golang.org/x/tools/gopls/internal/util/pathutil"
32
+ "golang.org/x/tools/gopls/internal/util/slices"
32
33
"golang.org/x/tools/gopls/internal/vulncheck"
33
34
"golang.org/x/tools/internal/event"
34
35
"golang.org/x/tools/internal/gocommand"
@@ -100,21 +101,12 @@ type View struct {
100
101
// ignoreFilter is used for fast checking of ignored files.
101
102
ignoreFilter * ignoreFilter
102
103
103
- // initCancelFirstAttempt can be used to terminate the view's first
104
+ // cancelInitialWorkspaceLoad can be used to terminate the view's first
104
105
// attempt at initialization.
105
- initCancelFirstAttempt context.CancelFunc
106
+ cancelInitialWorkspaceLoad context.CancelFunc
106
107
107
- // Track the latest snapshot via the snapshot field, guarded by snapshotMu.
108
- //
109
- // Invariant: whenever the snapshot field is overwritten, destroy(snapshot)
110
- // is called on the previous (overwritten) snapshot while snapshotMu is held,
111
- // incrementing snapshotWG. During shutdown the final snapshot is
112
- // overwritten with nil and destroyed, guaranteeing that all observed
113
- // snapshots have been destroyed via the destroy method, and snapshotWG may
114
- // be waited upon to let these destroy operations complete.
115
108
snapshotMu sync.Mutex
116
- snapshot * Snapshot // latest snapshot; nil after shutdown has been called
117
- snapshotWG sync.WaitGroup // refcount for pending destroy operations
109
+ snapshot * Snapshot // latest snapshot; nil after shutdown has been called
118
110
119
111
// initialWorkspaceLoad is closed when the first workspace initialization has
120
112
// completed. If we failed to load, we only retry if the go.mod file changes,
@@ -513,11 +505,10 @@ func (v *View) filterFunc() func(protocol.DocumentURI) bool {
513
505
}
514
506
}
515
507
516
- // shutdown releases resources associated with the view, and waits for ongoing
517
- // work to complete.
508
+ // shutdown releases resources associated with the view.
518
509
func (v * View ) shutdown () {
519
510
// Cancel the initial workspace load if it is still running.
520
- v .initCancelFirstAttempt ()
511
+ v .cancelInitialWorkspaceLoad ()
521
512
522
513
v .snapshotMu .Lock ()
523
514
if v .snapshot != nil {
@@ -526,8 +517,6 @@ func (v *View) shutdown() {
526
517
v .snapshot = nil
527
518
}
528
519
v .snapshotMu .Unlock ()
529
-
530
- v .snapshotWG .Wait ()
531
520
}
532
521
533
522
// IgnoredFile reports if a file would be ignored by a `go list` of the whole
@@ -767,16 +756,33 @@ type StateChange struct {
767
756
GCDetails map [metadata.PackageID ]bool // package -> whether or not we want details
768
757
}
769
758
770
- // Invalidate processes the provided state change, invalidating any derived
759
+ // InvalidateView processes the provided state change, invalidating any derived
771
760
// results that depend on the changed state.
772
761
//
773
762
// The resulting snapshot is non-nil, representing the outcome of the state
774
763
// change. The second result is a function that must be called to release the
775
764
// snapshot when the snapshot is no longer needed.
776
765
//
777
- // The resulting bool reports whether the new View needs to be re-diagnosed.
778
- // See Snapshot.clone for more details.
779
- func (v * View ) Invalidate (ctx context.Context , changed StateChange ) (* Snapshot , func (), bool ) {
766
+ // An error is returned if the given view is no longer active in the session.
767
+ func (s * Session ) InvalidateView (ctx context.Context , view * View , changed StateChange ) (* Snapshot , func (), error ) {
768
+ s .viewMu .Lock ()
769
+ defer s .viewMu .Unlock ()
770
+
771
+ if ! slices .Contains (s .views , view ) {
772
+ return nil , nil , fmt .Errorf ("view is no longer active" )
773
+ }
774
+ snapshot , release , _ := s .invalidateViewLocked (ctx , view , changed )
775
+ return snapshot , release , nil
776
+ }
777
+
778
+ // invalidateViewLocked invalidates the content of the given view.
779
+ // (See [Session.InvalidateView]).
780
+ //
781
+ // The resulting bool reports whether the View needs to be re-diagnosed.
782
+ // (See [Snapshot.clone]).
783
+ //
784
+ // s.viewMu must be held while calling this method.
785
+ func (s * Session ) invalidateViewLocked (ctx context.Context , v * View , changed StateChange ) (* Snapshot , func (), bool ) {
780
786
// Detach the context so that content invalidation cannot be canceled.
781
787
ctx = xcontext .Detach (ctx )
782
788
@@ -799,9 +805,9 @@ func (v *View) Invalidate(ctx context.Context, changed StateChange) (*Snapshot,
799
805
// TODO(rfindley): shouldn't we do this before canceling?
800
806
prevSnapshot .AwaitInitialized (ctx )
801
807
802
- // Save one lease of the cloned snapshot in the view.
803
808
var needsDiagnosis bool
804
- v .snapshot , needsDiagnosis = prevSnapshot .clone (ctx , v .baseCtx , changed )
809
+ s .snapshotWG .Add (1 )
810
+ v .snapshot , needsDiagnosis = prevSnapshot .clone (ctx , v .baseCtx , changed , s .snapshotWG .Done )
805
811
806
812
// Remove the initial reference created when prevSnapshot was created.
807
813
prevSnapshot .decref ()
0 commit comments