mirror of
https://github.com/golang/go
synced 2024-11-18 16:04:44 -07:00
internal/imports: fix use of uninitialized resolvers
Resolvers are lazy initialized. That worked fine until the addition of the scan semaphore -- it's not a good idea to create that lazily, since you can't synchronize on a channel that doesn't exist. Specifically, this caused a gopls hang when completion finished without needing to use the resolver. In that case, we'd call ClearForNewScan/Mod on an uninitialized resolver, and then hang receiving from a nil channel. Instead, eagerly initialize where convenient, and particularly the scan semaphore. Change-Id: I3adb1ae76b751650995e50f50346e06fd9c9f88d Reviewed-on: https://go-review.googlesource.com/c/tools/+/214258 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
0a1579a33b
commit
563860d11d
@ -754,10 +754,10 @@ func (e *ProcessEnv) GetResolver() Resolver {
|
||||
}
|
||||
out, err := e.invokeGo("env", "GOMOD")
|
||||
if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 {
|
||||
e.resolver = &gopathResolver{env: e}
|
||||
e.resolver = newGopathResolver(e)
|
||||
return e.resolver
|
||||
}
|
||||
e.resolver = &ModuleResolver{env: e}
|
||||
e.resolver = newModuleResolver(e)
|
||||
return e.resolver
|
||||
}
|
||||
|
||||
@ -998,31 +998,30 @@ type gopathResolver struct {
|
||||
scanSema chan struct{} // scanSema prevents concurrent scans.
|
||||
}
|
||||
|
||||
func (r *gopathResolver) init() {
|
||||
if r.cache == nil {
|
||||
r.cache = &dirInfoCache{
|
||||
func newGopathResolver(env *ProcessEnv) *gopathResolver {
|
||||
r := &gopathResolver{
|
||||
env: env,
|
||||
cache: &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
listeners: map[*int]cacheListener{},
|
||||
}
|
||||
}
|
||||
if r.scanSema == nil {
|
||||
r.scanSema = make(chan struct{}, 1)
|
||||
r.scanSema <- struct{}{}
|
||||
},
|
||||
scanSema: make(chan struct{}, 1),
|
||||
}
|
||||
r.scanSema <- struct{}{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *gopathResolver) ClearForNewScan() {
|
||||
<-r.scanSema
|
||||
*r = gopathResolver{
|
||||
env: r.env,
|
||||
scanSema: r.scanSema,
|
||||
r.cache = &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
listeners: map[*int]cacheListener{},
|
||||
}
|
||||
r.init()
|
||||
r.walked = false
|
||||
r.scanSema <- struct{}{}
|
||||
}
|
||||
|
||||
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||
r.init()
|
||||
names := map[string]string{}
|
||||
for _, path := range importPaths {
|
||||
names[path] = importPathToName(r.env, path, srcDir)
|
||||
@ -1150,7 +1149,6 @@ func distance(basepath, targetpath string) int {
|
||||
}
|
||||
|
||||
func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error {
|
||||
r.init()
|
||||
add := func(root gopathwalk.Root, dir string) {
|
||||
// We assume cached directories have not changed. We can skip them and their
|
||||
// children.
|
||||
@ -1234,7 +1232,6 @@ func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []
|
||||
}
|
||||
|
||||
func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg) (string, []string, error) {
|
||||
r.init()
|
||||
if info, ok := r.cache.Load(pkg.dir); ok {
|
||||
return r.cache.CacheExports(ctx, r.env, info)
|
||||
}
|
||||
|
@ -49,6 +49,15 @@ type ModuleJSON struct {
|
||||
GoVersion string // go version used in module
|
||||
}
|
||||
|
||||
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
|
||||
r := &ModuleResolver{
|
||||
env: e,
|
||||
scanSema: make(chan struct{}, 1),
|
||||
}
|
||||
r.scanSema <- struct{}{}
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *ModuleResolver) init() error {
|
||||
if r.Initialized {
|
||||
return nil
|
||||
@ -120,10 +129,6 @@ func (r *ModuleResolver) init() error {
|
||||
}
|
||||
|
||||
r.scannedRoots = map[gopathwalk.Root]bool{}
|
||||
if r.scanSema == nil {
|
||||
r.scanSema = make(chan struct{}, 1)
|
||||
r.scanSema <- struct{}{}
|
||||
}
|
||||
if r.moduleCacheCache == nil {
|
||||
r.moduleCacheCache = &dirInfoCache{
|
||||
dirs: map[string]*directoryPackageInfo{},
|
||||
|
@ -239,7 +239,7 @@ import _ "rsc.io/sampler"
|
||||
}
|
||||
|
||||
// Clear out the resolver's cache, since we've changed the environment.
|
||||
mt.resolver = &ModuleResolver{env: mt.env}
|
||||
mt.resolver = newModuleResolver(mt.env)
|
||||
mt.env.GOFLAGS = "-mod=vendor"
|
||||
mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `/vendor/`)
|
||||
}
|
||||
@ -694,7 +694,7 @@ func setup(t *testing.T, main, wd string) *modTest {
|
||||
return &modTest{
|
||||
T: t,
|
||||
env: env,
|
||||
resolver: &ModuleResolver{env: env},
|
||||
resolver: newModuleResolver(env),
|
||||
cleanup: func() { removeDir(dir) },
|
||||
}
|
||||
}
|
||||
@ -851,7 +851,7 @@ func TestInvalidModCache(t *testing.T) {
|
||||
GOSUMDB: "off",
|
||||
WorkingDir: dir,
|
||||
}
|
||||
resolver := &ModuleResolver{env: env}
|
||||
resolver := newModuleResolver(env)
|
||||
scanToSlice(resolver, nil)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user