Skip to content

Commit 563860d

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rebecca Stambler <[email protected]>
1 parent 0a1579a commit 563860d

File tree

3 files changed

+26
-24
lines changed

3 files changed

+26
-24
lines changed

internal/imports/fix.go

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,10 @@ func (e *ProcessEnv) GetResolver() Resolver {
754754
}
755755
out, err := e.invokeGo("env", "GOMOD")
756756
if err != nil || len(bytes.TrimSpace(out.Bytes())) == 0 {
757-
e.resolver = &gopathResolver{env: e}
757+
e.resolver = newGopathResolver(e)
758758
return e.resolver
759759
}
760-
e.resolver = &ModuleResolver{env: e}
760+
e.resolver = newModuleResolver(e)
761761
return e.resolver
762762
}
763763

@@ -998,31 +998,30 @@ type gopathResolver struct {
998998
scanSema chan struct{} // scanSema prevents concurrent scans.
999999
}
10001000

1001-
func (r *gopathResolver) init() {
1002-
if r.cache == nil {
1003-
r.cache = &dirInfoCache{
1001+
func newGopathResolver(env *ProcessEnv) *gopathResolver {
1002+
r := &gopathResolver{
1003+
env: env,
1004+
cache: &dirInfoCache{
10041005
dirs: map[string]*directoryPackageInfo{},
10051006
listeners: map[*int]cacheListener{},
1006-
}
1007-
}
1008-
if r.scanSema == nil {
1009-
r.scanSema = make(chan struct{}, 1)
1010-
r.scanSema <- struct{}{}
1007+
},
1008+
scanSema: make(chan struct{}, 1),
10111009
}
1010+
r.scanSema <- struct{}{}
1011+
return r
10121012
}
10131013

10141014
func (r *gopathResolver) ClearForNewScan() {
10151015
<-r.scanSema
1016-
*r = gopathResolver{
1017-
env: r.env,
1018-
scanSema: r.scanSema,
1016+
r.cache = &dirInfoCache{
1017+
dirs: map[string]*directoryPackageInfo{},
1018+
listeners: map[*int]cacheListener{},
10191019
}
1020-
r.init()
1020+
r.walked = false
10211021
r.scanSema <- struct{}{}
10221022
}
10231023

10241024
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
1025-
r.init()
10261025
names := map[string]string{}
10271026
for _, path := range importPaths {
10281027
names[path] = importPathToName(r.env, path, srcDir)
@@ -1150,7 +1149,6 @@ func distance(basepath, targetpath string) int {
11501149
}
11511150

11521151
func (r *gopathResolver) scan(ctx context.Context, callback *scanCallback) error {
1153-
r.init()
11541152
add := func(root gopathwalk.Root, dir string) {
11551153
// We assume cached directories have not changed. We can skip them and their
11561154
// children.
@@ -1234,7 +1232,6 @@ func filterRoots(roots []gopathwalk.Root, include func(gopathwalk.Root) bool) []
12341232
}
12351233

12361234
func (r *gopathResolver) loadExports(ctx context.Context, pkg *pkg) (string, []string, error) {
1237-
r.init()
12381235
if info, ok := r.cache.Load(pkg.dir); ok {
12391236
return r.cache.CacheExports(ctx, r.env, info)
12401237
}

internal/imports/mod.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ type ModuleJSON struct {
4949
GoVersion string // go version used in module
5050
}
5151

52+
func newModuleResolver(e *ProcessEnv) *ModuleResolver {
53+
r := &ModuleResolver{
54+
env: e,
55+
scanSema: make(chan struct{}, 1),
56+
}
57+
r.scanSema <- struct{}{}
58+
return r
59+
}
60+
5261
func (r *ModuleResolver) init() error {
5362
if r.Initialized {
5463
return nil
@@ -120,10 +129,6 @@ func (r *ModuleResolver) init() error {
120129
}
121130

122131
r.scannedRoots = map[gopathwalk.Root]bool{}
123-
if r.scanSema == nil {
124-
r.scanSema = make(chan struct{}, 1)
125-
r.scanSema <- struct{}{}
126-
}
127132
if r.moduleCacheCache == nil {
128133
r.moduleCacheCache = &dirInfoCache{
129134
dirs: map[string]*directoryPackageInfo{},

internal/imports/mod_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ import _ "rsc.io/sampler"
239239
}
240240

241241
// Clear out the resolver's cache, since we've changed the environment.
242-
mt.resolver = &ModuleResolver{env: mt.env}
242+
mt.resolver = newModuleResolver(mt.env)
243243
mt.env.GOFLAGS = "-mod=vendor"
244244
mt.assertModuleFoundInDir("rsc.io/sampler", "sampler", `/vendor/`)
245245
}
@@ -694,7 +694,7 @@ func setup(t *testing.T, main, wd string) *modTest {
694694
return &modTest{
695695
T: t,
696696
env: env,
697-
resolver: &ModuleResolver{env: env},
697+
resolver: newModuleResolver(env),
698698
cleanup: func() { removeDir(dir) },
699699
}
700700
}
@@ -851,7 +851,7 @@ func TestInvalidModCache(t *testing.T) {
851851
GOSUMDB: "off",
852852
WorkingDir: dir,
853853
}
854-
resolver := &ModuleResolver{env: env}
854+
resolver := newModuleResolver(env)
855855
scanToSlice(resolver, nil)
856856
}
857857

0 commit comments

Comments
 (0)