gopls/internal/lsp/cache: compute xrefs and methodsets asynchronously
No need to wait on xrefs or methodsets while performing
latency-sensitive operations on packages.
For golang/go#53992
Change-Id: I9ea65269a8c1e604fb99ed8b25e14db79f179576
Reviewed-on: https://go-review.googlesource.com/c/tools/+/503015
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index 5f5400b..6cfabe3 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -26,9 +26,7 @@
"golang.org/x/tools/gopls/internal/lsp/filecache"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/source"
- "golang.org/x/tools/gopls/internal/lsp/source/methodsets"
"golang.org/x/tools/gopls/internal/lsp/source/typerefs"
- "golang.org/x/tools/gopls/internal/lsp/source/xrefs"
"golang.org/x/tools/gopls/internal/span"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/tag"
@@ -44,6 +42,8 @@
preserveImportGraph = true // hold on to the import graph for open packages
)
+type unit = struct{}
+
// A typeCheckBatch holds data for a logical type-checking operation, which may
// type-check many unrelated packages.
//
@@ -56,7 +56,7 @@
handles map[PackageID]*packageHandle
parseCache *parseCache
fset *token.FileSet // describes all parsed or imported files
- cpulimit chan struct{} // concurrency limiter for CPU-bound operations
+ cpulimit chan unit // concurrency limiter for CPU-bound operations
mu sync.Mutex
syntaxPackages map[PackageID]*futurePackage // results of processing a requested package; may hold (nil, nil)
@@ -69,7 +69,7 @@
// The goroutine that creates the futurePackage is responsible for evaluating
// its value, and closing the done channel.
type futurePackage struct {
- done chan struct{}
+ done chan unit
v pkgOrErr
}
@@ -154,7 +154,7 @@
// for the work to be done.
done := s.importGraphDone
if done == nil {
- done = make(chan struct{})
+ done = make(chan unit)
s.importGraphDone = done
release := s.Acquire() // must acquire to use the snapshot asynchronously
go func() {
@@ -360,7 +360,7 @@
handles: handles,
fset: fileSetWithBase(reservedForParsing),
syntaxIndex: make(map[PackageID]int),
- cpulimit: make(chan struct{}, runtime.GOMAXPROCS(0)),
+ cpulimit: make(chan unit, runtime.GOMAXPROCS(0)),
syntaxPackages: make(map[PackageID]*futurePackage),
importPackages: make(map[PackageID]*futurePackage),
}
@@ -369,7 +369,7 @@
// Clone the file set every time, to ensure we do not leak files.
b.fset = tokeninternal.CloneFileSet(importGraph.fset)
// Pre-populate future cache with 'done' futures.
- done := make(chan struct{})
+ done := make(chan unit)
close(done)
for id, res := range importGraph.imports {
b.importPackages[id] = &futurePackage{done, res}
@@ -427,7 +427,7 @@
}
}
- f = &futurePackage{done: make(chan struct{})}
+ f = &futurePackage{done: make(chan unit)}
b.importPackages[id] = f
b.mu.Unlock()
@@ -479,7 +479,7 @@
return f.v.pkg, f.v.err
}
- f = &futurePackage{done: make(chan struct{})}
+ f = &futurePackage{done: make(chan unit)}
b.syntaxPackages[id] = f
b.mu.Unlock()
defer func() {
@@ -508,7 +508,7 @@
select {
case <-ctx.Done():
return nil, ctx.Err()
- case b.cpulimit <- struct{}{}:
+ case b.cpulimit <- unit{}:
defer func() {
<-b.cpulimit // release CPU token
}()
@@ -637,8 +637,8 @@
// Write package data to disk asynchronously.
go func() {
toCache := map[string][]byte{
- xrefsKind: pkg.xrefs,
- methodSetsKind: pkg.methodsets.Encode(),
+ xrefsKind: pkg.xrefs(),
+ methodSetsKind: pkg.methodsets().Encode(),
diagnosticsKind: encodeDiagnostics(pkg.diagnostics),
}
@@ -763,13 +763,13 @@
// Collect all reachable IDs, and create done channels.
// TODO: opt: modify SortPostOrder to make this pre-traversal unnecessary.
var allIDs []PackageID
- dones := make(map[PackageID]chan struct{})
+ dones := make(map[PackageID]chan unit)
var walk func(PackageID)
walk = func(id PackageID) {
if _, ok := dones[id]; ok {
return
}
- dones[id] = make(chan struct{})
+ dones[id] = make(chan unit)
allIDs = append(allIDs, id)
m := meta.metadata[id]
for _, depID := range m.DepsByPkgPath {
@@ -1262,8 +1262,6 @@
if err != nil {
return nil, err
}
- pkg.methodsets = methodsets.NewIndex(pkg.fset, pkg.types)
- pkg.xrefs = xrefs.Index(pkg.compiledGoFiles, pkg.types, pkg.typesInfo)
// Our heuristic for whether to show type checking errors is:
// + If any file was 'fixed', don't show type checking errors as we
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index dbbb4be..32f6349 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -11,9 +11,11 @@
"go/scanner"
"go/token"
"go/types"
+ "sync"
"golang.org/x/tools/gopls/internal/lsp/source"
"golang.org/x/tools/gopls/internal/lsp/source/methodsets"
+ "golang.org/x/tools/gopls/internal/lsp/source/xrefs"
"golang.org/x/tools/gopls/internal/span"
)
@@ -53,11 +55,25 @@
importMap map[PackagePath]*types.Package
hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors
- // TODO(rfindley): opt: xrefs and methodsets do not need to be pinned to the
- // package, and perhaps should be computed asynchronously to package
- // diagnostics.
- xrefs []byte
- methodsets *methodsets.Index
+ xrefsOnce sync.Once
+ _xrefs []byte // only used by the xrefs method
+
+ methodsetsOnce sync.Once
+ _methodsets *methodsets.Index // only used by the methodsets method
+}
+
+func (p *syntaxPackage) xrefs() []byte {
+ p.xrefsOnce.Do(func() {
+ p._xrefs = xrefs.Index(p.compiledGoFiles, p.types, p.typesInfo)
+ })
+ return p._xrefs
+}
+
+func (p *syntaxPackage) methodsets() *methodsets.Index {
+ p.methodsetsOnce.Do(func() {
+ p._methodsets = methodsets.NewIndex(p.fset, p.types)
+ })
+ return p._methodsets
}
func (p *Package) String() string { return string(p.ph.m.ID) }
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index bff3dc1..6ff14fb 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -688,7 +688,7 @@
return true
}
post := func(i int, pkg *Package) {
- indexes[i] = XrefIndex{m: pkg.ph.m, data: pkg.pkg.xrefs}
+ indexes[i] = XrefIndex{m: pkg.ph.m, data: pkg.pkg.xrefs()}
}
return indexes, s.forEachPackage(ctx, ids, pre, post)
}
@@ -719,7 +719,7 @@
return true
}
post := func(i int, pkg *Package) {
- indexes[i] = pkg.pkg.methodsets
+ indexes[i] = pkg.pkg.methodsets()
}
return indexes, s.forEachPackage(ctx, ids, pre, post)
}