gopls/internal/cache: stop module cache refresh on view shutdown
As described in golang/go#67865, CL 590377 exacerbated a problem that
module cache refreshes may outlive the lifecycle of the view.
Fixes golang/go#67865
Change-Id: Ieafdf6601fee00b6e8ce705502a80224da071578
Reviewed-on: https://go-review.googlesource.com/c/tools/+/591315
Auto-Submit: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/cache/imports.go b/gopls/internal/cache/imports.go
index 76a5855..c467a85 100644
--- a/gopls/internal/cache/imports.go
+++ b/gopls/internal/cache/imports.go
@@ -35,6 +35,18 @@
}
}
+// stop stops any future scheduled refresh.
+func (t *refreshTimer) stop() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+
+ if t.timer != nil {
+ t.timer.Stop()
+ t.timer = nil
+ t.refreshFn = nil // release resources
+ }
+}
+
// schedule schedules the refresh function to run at some point in the future,
// if no existing refresh is already scheduled.
//
@@ -54,11 +66,16 @@
}
t.timer = time.AfterFunc(delay, func() {
start := time.Now()
- t.refreshFn()
t.mu.Lock()
- t.duration = time.Since(start)
- t.timer = nil
+ refreshFn := t.refreshFn
t.mu.Unlock()
+ if refreshFn != nil { // timer may be stopped.
+ refreshFn()
+ t.mu.Lock()
+ t.duration = time.Since(start)
+ t.timer = nil
+ t.mu.Unlock()
+ }
})
}
}
@@ -70,7 +87,8 @@
type sharedModCache struct {
mu sync.Mutex
caches map[string]*imports.DirInfoCache // GOMODCACHE -> cache content; never invalidated
- timers map[string]*refreshTimer // GOMODCACHE -> timer
+ // TODO(rfindley): consider stopping these timers when the session shuts down.
+ timers map[string]*refreshTimer // GOMODCACHE -> timer
}
func (c *sharedModCache) dirCache(dir string) *imports.DirInfoCache {
@@ -131,6 +149,11 @@
return s
}
+// stopTimer stops scheduled refreshes of this imports state.
+func (s *importsState) stopTimer() {
+ s.refreshTimer.stop()
+}
+
// runProcessEnvFunc runs goimports.
//
// Any call to runProcessEnvFunc will schedule a refresh of the imports state
diff --git a/gopls/internal/cache/view.go b/gopls/internal/cache/view.go
index 6f76c55..ab77e63 100644
--- a/gopls/internal/cache/view.go
+++ b/gopls/internal/cache/view.go
@@ -470,6 +470,7 @@
func (v *View) shutdown() {
// Cancel the initial workspace load if it is still running.
v.cancelInitialWorkspaceLoad()
+ v.importsState.stopTimer()
v.snapshotMu.Lock()
if v.snapshot != nil {