mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
tools/internal/imports: fix data race in packageInfo
Before this commit, when running imports.Process concurrently, the program panics with a fatal error due to concurrent map iterations and map writes. This CL fixes this by adding a copy of the map to the packageInfo structure. Fixed #34895 Change-Id: If009e6108813f86495c7e20e69739186b8b236d7 Reviewed-on: https://go-review.googlesource.com/c/tools/+/200865 Run-TryBot: Heschi Kreinick <heschi@google.com> Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
parent
341939e086
commit
1f7a813c0d
@ -475,9 +475,9 @@ func (p *pass) assumeSiblingImportsValid() {
|
|||||||
}
|
}
|
||||||
for left, rights := range refs {
|
for left, rights := range refs {
|
||||||
if imp, ok := importsByName[left]; ok {
|
if imp, ok := importsByName[left]; ok {
|
||||||
if _, ok := stdlib[imp.ImportPath]; ok {
|
if m, ok := stdlib[imp.ImportPath]; ok {
|
||||||
// We have the stdlib in memory; no need to guess.
|
// We have the stdlib in memory; no need to guess.
|
||||||
rights = stdlib[imp.ImportPath]
|
rights = copyExports(m)
|
||||||
}
|
}
|
||||||
p.addCandidate(imp, &packageInfo{
|
p.addCandidate(imp, &packageInfo{
|
||||||
// no name; we already know it.
|
// no name; we already know it.
|
||||||
@ -712,9 +712,10 @@ func cmdDebugStr(cmd *exec.Cmd) string {
|
|||||||
|
|
||||||
func addStdlibCandidates(pass *pass, refs references) {
|
func addStdlibCandidates(pass *pass, refs references) {
|
||||||
add := func(pkg string) {
|
add := func(pkg string) {
|
||||||
|
exports := copyExports(stdlib[pkg])
|
||||||
pass.addCandidate(
|
pass.addCandidate(
|
||||||
&ImportInfo{ImportPath: pkg},
|
&ImportInfo{ImportPath: pkg},
|
||||||
&packageInfo{name: path.Base(pkg), exports: stdlib[pkg]})
|
&packageInfo{name: path.Base(pkg), exports: exports})
|
||||||
}
|
}
|
||||||
for left := range refs {
|
for left := range refs {
|
||||||
if left == "rand" {
|
if left == "rand" {
|
||||||
@ -1383,3 +1384,11 @@ type visitFn func(node ast.Node) ast.Visitor
|
|||||||
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
|
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
|
||||||
return fn(node)
|
return fn(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyExports(pkg map[string]bool) map[string]bool {
|
||||||
|
m := make(map[string]bool, len(pkg))
|
||||||
|
for k, v := range pkg {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/tools/go/packages/packagestest"
|
"golang.org/x/tools/go/packages/packagestest"
|
||||||
@ -1680,8 +1681,9 @@ func (t *goimportTest) processNonModule(file string, contents []byte, opts *Opti
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
opts = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||||
}
|
}
|
||||||
opts.Env = t.env
|
// ProcessEnv is not safe for concurrent use. Make a copy.
|
||||||
opts.Env.Debug = *testDebug
|
env := *t.env
|
||||||
|
opts.Env = &env
|
||||||
return Process(file, contents, opts)
|
return Process(file, contents, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2539,3 +2541,45 @@ func TestStdLibGetCandidates(t *testing.T) {
|
|||||||
t.Errorf("expected to find candidate with path: %q, name: %q next in ordered scan of results`", want[wantIdx].wantPkg, want[wantIdx].wantName)
|
t.Errorf("expected to find candidate with path: %q, name: %q next in ordered scan of results`", want[wantIdx].wantPkg, want[wantIdx].wantName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests #34895: process should not panic on concurrent calls.
|
||||||
|
func TestConcurrentProcess(t *testing.T) {
|
||||||
|
testConfig{
|
||||||
|
module: packagestest.Module{
|
||||||
|
Name: "foo.com",
|
||||||
|
Files: fm{
|
||||||
|
"p/first.go": `package foo
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"p/second.go": `package foo
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
fmt.Println()
|
||||||
|
imports.Bar() // not imported.
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}.test(t, func(t *goimportTest) {
|
||||||
|
var (
|
||||||
|
n = 10
|
||||||
|
wg sync.WaitGroup
|
||||||
|
)
|
||||||
|
wg.Add(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
_, err := t.process("foo.com", "p/first.go", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user