1
0
mirror of https://github.com/golang/go synced 2024-11-18 16:14:46 -07:00

go.tools/go/ssa: fix a race condition in needMethodsOf.

Now all calls are serialized with a mutex.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/140600043
This commit is contained in:
Alan Donovan 2014-09-15 14:01:54 -04:00
parent b94e29089b
commit f13b4c029c
3 changed files with 21 additions and 16 deletions

View File

@ -2293,17 +2293,20 @@ func (p *Package) typeOf(e ast.Expr) types.Type {
//
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
//
// TODO(adonovan): fix: make this thread-safe. I don't think it is
// right now since it's called concurrently from emitConv.
// Thread-safe. (Called via emitConv from multiple builder goroutines.)
//
// TODO(adonovan): make this faster. It accounts for 20% of SSA build
// time, even with concurrency and no locking.
// time. Do we need to maintain a distinct needRTTI and methodSets per
// package? Using just one in the program might be much faster.
//
func (p *Package) needMethodsOf(T types.Type) {
p.methodsMu.Lock()
p.needMethods(T, false)
p.methodsMu.Unlock()
}
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
// Precondition: the p.methodsMu lock is held.
// Recursive case: skip => don't call makeMethods(T).
func (p *Package) needMethods(T types.Type, skip bool) {
// Each package maintains its own set of types it has visited.
@ -2346,8 +2349,8 @@ func (p *Package) needMethods(T types.Type, skip bool) {
tmset := p.Prog.MethodSets.MethodSet(T)
for i := 0; i < tmset.Len(); i++ {
sig := tmset.At(i).Type().(*types.Signature)
p.needMethodsOf(sig.Params())
p.needMethodsOf(sig.Results())
p.needMethods(sig.Params(), false)
p.needMethods(sig.Results(), false)
}
switch t := T.(type) {
@ -2358,29 +2361,29 @@ func (p *Package) needMethods(T types.Type, skip bool) {
// nop---handled by recursion over method set.
case *types.Pointer:
p.needMethodsOf(t.Elem())
p.needMethods(t.Elem(), false)
case *types.Slice:
p.needMethodsOf(t.Elem())
p.needMethods(t.Elem(), false)
case *types.Chan:
p.needMethodsOf(t.Elem())
p.needMethods(t.Elem(), false)
case *types.Map:
p.needMethodsOf(t.Key())
p.needMethodsOf(t.Elem())
p.needMethods(t.Key(), false)
p.needMethods(t.Elem(), false)
case *types.Signature:
if t.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
}
p.needMethodsOf(t.Params())
p.needMethodsOf(t.Results())
p.needMethods(t.Params(), false)
p.needMethods(t.Results(), false)
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
p.needMethodsOf(types.NewPointer(T))
p.needMethods(types.NewPointer(T), false)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
@ -2389,16 +2392,16 @@ func (p *Package) needMethods(T types.Type, skip bool) {
p.needMethods(t.Underlying(), true)
case *types.Array:
p.needMethodsOf(t.Elem())
p.needMethods(t.Elem(), false)
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
p.needMethodsOf(t.Field(i).Type())
p.needMethods(t.Field(i).Type(), false)
}
case *types.Tuple:
for i, n := 0, t.Len(); i < n; i++ {
p.needMethodsOf(t.At(i).Type())
p.needMethods(t.At(i).Type(), false)
}
default:

View File

@ -182,6 +182,7 @@ func (prog *Program) TypesWithMethodSets() []types.Type {
// Callers must not mutate the result.
//
func (pkg *Package) TypesWithMethodSets() []types.Type {
// pkg.methodsMu not required; concurrent (build) phase is over.
return pkg.methodSets
}

View File

@ -43,6 +43,7 @@ type Package struct {
Prog *Program // the owning program
Object *types.Package // the type checker's package object for this package
Members map[string]Member // all package members keyed by name
methodsMu sync.Mutex // guards needRTTI and methodSets
methodSets []types.Type // types whose method sets are included in this package
values map[types.Object]Value // package members (incl. types and methods), keyed by object
init *Function // Func("init"); the package's init function