internal/lsp: add a mutex around the view's options
The options can be modified without the view being recreated, so we need
a mutex there.
Change-Id: I87e881835622a941fce98e4a1062aa41acd84fcb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/227022
Reviewed-by: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/os_darwin.go b/internal/lsp/cache/os_darwin.go
index 9b31b0c..73c26fd 100644
--- a/internal/lsp/cache/os_darwin.go
+++ b/internal/lsp/cache/os_darwin.go
@@ -1,6 +1,7 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+
package cache
import (
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index be8e5a9..23a54ea 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -89,8 +89,12 @@
// Config returns the configuration used for the snapshot's interaction with the
// go/packages API.
func (s *snapshot) Config(ctx context.Context) *packages.Config {
- env, buildFlags := s.view.env()
- cfg := &packages.Config{
+ s.view.optionsMu.Lock()
+ env, buildFlags := s.view.envLocked()
+ verboseOutput := s.view.options.VerboseOutput
+ s.view.optionsMu.Unlock()
+
+ return &packages.Config{
Env: env,
Dir: s.view.folder.Filename(),
Context: ctx,
@@ -107,13 +111,12 @@
panic("go/packages must not be used to parse files")
},
Logf: func(format string, args ...interface{}) {
- if s.view.options.VerboseOutput {
+ if verboseOutput {
event.Print(ctx, fmt.Sprintf(format, args...))
}
},
Tests: true,
}
- return cfg
}
func (s *snapshot) buildOverlay() map[string][]byte {
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 6fb717d..4d81b9e 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -37,7 +37,8 @@
session *Session
id string
- options source.Options
+ optionsMu sync.Mutex
+ options source.Options
// mu protects most mutable state of the view.
mu sync.Mutex
@@ -172,6 +173,8 @@
}
func (v *view) Options() source.Options {
+ v.optionsMu.Lock()
+ defer v.optionsMu.Unlock()
return v.options
}
@@ -189,16 +192,19 @@
func (v *view) SetOptions(ctx context.Context, options source.Options) (source.View, error) {
// no need to rebuild the view if the options were not materially changed
+ v.optionsMu.Lock()
if minorOptionsChange(v.options, options) {
v.options = options
+ v.optionsMu.Unlock()
return v, nil
}
+ v.optionsMu.Unlock()
newView, _, err := v.session.updateView(ctx, v, options)
return newView, err
}
func (v *view) Rebuild(ctx context.Context) (source.Snapshot, error) {
- _, snapshot, err := v.session.updateView(ctx, v, v.options)
+ _, snapshot, err := v.session.updateView(ctx, v, v.Options())
return snapshot, err
}
@@ -263,7 +269,9 @@
}
func (v *view) WriteEnv(ctx context.Context, w io.Writer) error {
- env, buildFlags := v.env()
+ v.optionsMu.Lock()
+ env, buildFlags := v.envLocked()
+ v.optionsMu.Unlock()
// TODO(rstambler): We could probably avoid running this by saving the
// output on original create, but I'm not sure if it's worth it.
inv := gocommand.Invocation{
@@ -348,13 +356,16 @@
}
func (v *view) buildProcessEnv(ctx context.Context) (*imports.ProcessEnv, error) {
- env, buildFlags := v.env()
+ v.optionsMu.Lock()
+ env, buildFlags := v.envLocked()
+ localPrefix, verboseOutput := v.options.LocalPrefix, v.options.VerboseOutput
+ v.optionsMu.Unlock()
processEnv := &imports.ProcessEnv{
WorkingDir: v.folder.Filename(),
BuildFlags: buildFlags,
- LocalPrefix: v.options.LocalPrefix,
+ LocalPrefix: localPrefix,
}
- if v.options.VerboseOutput {
+ if verboseOutput {
processEnv.Logf = func(format string, args ...interface{}) {
event.Print(ctx, fmt.Sprintf(format, args...))
}
@@ -382,7 +393,7 @@
return processEnv, nil
}
-func (v *view) env() ([]string, []string) {
+func (v *view) envLocked() ([]string, []string) {
// We want to run the go commands with the -modfile flag if the version of go
// that we are using supports it.
buildFlags := v.options.BuildFlags