internal/lsp: using memoize for all file contents
Change-Id: I7293bbcfea33c0df90f7b6df8e991aae9a03f2ab
Reviewed-on: https://go-review.googlesource.com/c/tools/+/180846
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/cache.go b/internal/lsp/cache/cache.go
index 81a8c77..b14fc9d 100644
--- a/internal/lsp/cache/cache.go
+++ b/internal/lsp/cache/cache.go
@@ -5,6 +5,7 @@
package cache
import (
+ "context"
"crypto/sha1"
"fmt"
"go/token"
@@ -14,6 +15,7 @@
"golang.org/x/tools/internal/lsp/debug"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/xlog"
+ "golang.org/x/tools/internal/memoize"
"golang.org/x/tools/internal/span"
)
@@ -32,10 +34,42 @@
fs source.FileSystem
id string
fset *token.FileSet
+
+ store memoize.Store
+}
+
+type fileKey struct {
+ identity source.FileIdentity
+}
+
+type fileHandle struct {
+ cache *cache
+ underlying source.FileHandle
+ handle *memoize.Handle
+}
+
+type fileData struct {
+ memoize.NoCopy
+ bytes []byte
+ hash string
+ err error
}
func (c *cache) GetFile(uri span.URI) source.FileHandle {
- return c.fs.GetFile(uri)
+ underlying := c.fs.GetFile(uri)
+ key := fileKey{
+ identity: underlying.Identity(),
+ }
+ h := c.store.Bind(key, func(ctx context.Context) interface{} {
+ data := &fileData{}
+ data.bytes, data.hash, data.err = underlying.Read(ctx)
+ return data
+ })
+ return &fileHandle{
+ cache: c,
+ underlying: underlying,
+ handle: h,
+ }
}
func (c *cache) NewSession(log xlog.Logger) source.Session {
@@ -55,6 +89,23 @@
return c.fset
}
+func (h *fileHandle) FileSystem() source.FileSystem {
+ return h.cache
+}
+
+func (h *fileHandle) Identity() source.FileIdentity {
+ return h.underlying.Identity()
+}
+
+func (h *fileHandle) Read(ctx context.Context) ([]byte, string, error) {
+ v := h.handle.Get(ctx)
+ if v == nil {
+ return nil, "", ctx.Err()
+ }
+ data := v.(*fileData)
+ return data.bytes, data.hash, data.err
+}
+
func hashContents(contents []byte) string {
// TODO: consider whether sha1 is the best choice here
// This hash is used for internal identity detection only
diff --git a/internal/lsp/cache/external.go b/internal/lsp/cache/external.go
index d2b8286..65f1cb5 100644
--- a/internal/lsp/cache/external.go
+++ b/internal/lsp/cache/external.go
@@ -42,6 +42,7 @@
}
func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) {
+ //TODO: this should fail if the version is not the same as the handle
data, err := ioutil.ReadFile(h.identity.URI.Filename())
if err != nil {
return nil, "", err
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 7d81fb0..40d3c1e 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -225,7 +225,11 @@
// including any position and type information that depends on it.
func (f *goFile) invalidateContent() {
f.view.pcache.mu.Lock()
- defer f.view.pcache.mu.Unlock()
+ f.handleMu.Lock()
+ defer func() {
+ f.handleMu.Unlock()
+ f.view.pcache.mu.Unlock()
+ }()
f.ast = nil
f.token = nil