Fix data race when applying replacements

Mutator context goroutines appending directly to the global Context's
replacements list causes a data race.  Send them over a channel
instead.

The renames and replacements are local to the mutator, so move them
out of Context and into the runMutator method.

Change-Id: I797edb1e27ee29f8946c58101b40fcfb50a32eb9
This commit is contained in:
Colin Cross 2016-12-09 10:29:05 -08:00
parent 0f1fa92e86
commit 0ce142ca05
2 changed files with 49 additions and 32 deletions

View file

@ -101,10 +101,6 @@ type Context struct {
// set lazily by sortedModuleNames // set lazily by sortedModuleNames
cachedSortedModuleNames []string cachedSortedModuleNames []string
// List of pending renames and replacements to apply after the mutator pass
renames []rename
replacements []replace
globs map[string]GlobPath globs map[string]GlobPath
globLock sync.Mutex globLock sync.Mutex
@ -1706,16 +1702,22 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
newModuleInfo[k] = v newModuleInfo[k] = v
} }
type globalStateChange struct {
reverse []reverseDep
rename []rename
replace []replace
}
reverseDeps := make(map[*moduleInfo][]depInfo) reverseDeps := make(map[*moduleInfo][]depInfo)
var rename []rename
var replace []replace
errsCh := make(chan []error) errsCh := make(chan []error)
reverseDepsCh := make(chan []reverseDep) globalStateCh := make(chan globalStateChange)
newModulesCh := make(chan []*moduleInfo) newModulesCh := make(chan []*moduleInfo)
done := make(chan bool) done := make(chan bool)
c.depsModified = 0 c.depsModified = 0
c.renames = nil
c.replacements = nil
visit := func(module *moduleInfo) bool { visit := func(module *moduleInfo) bool {
if module.splitModules != nil { if module.splitModules != nil {
@ -1755,8 +1757,12 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
newModulesCh <- mctx.newModules newModulesCh <- mctx.newModules
} }
if len(mctx.reverseDeps) > 0 { if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 {
reverseDepsCh <- mctx.reverseDeps globalStateCh <- globalStateChange{
reverse: mctx.reverseDeps,
replace: mctx.replace,
rename: mctx.rename,
}
} }
return false return false
@ -1768,10 +1774,12 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
select { select {
case newErrs := <-errsCh: case newErrs := <-errsCh:
errs = append(errs, newErrs...) errs = append(errs, newErrs...)
case newReverseDeps := <-reverseDepsCh: case globalStateChange := <-globalStateCh:
for _, r := range newReverseDeps { for _, r := range globalStateChange.reverse {
reverseDeps[r.module] = append(reverseDeps[r.module], r.dep) reverseDeps[r.module] = append(reverseDeps[r.module], r.dep)
} }
replace = append(replace, globalStateChange.replace...)
rename = append(rename, globalStateChange.rename...)
case newModules := <-newModulesCh: case newModules := <-newModulesCh:
for _, m := range newModules { for _, m := range newModules {
newModuleInfo[m.logicModule] = m newModuleInfo[m.logicModule] = m
@ -1821,7 +1829,12 @@ func (c *Context) runMutator(config interface{}, mutator *mutatorInfo,
c.depsModified++ c.depsModified++
} }
errs = c.handleRenamesAndReplacements() errs = c.handleRenames(rename)
if len(errs) > 0 {
return errs
}
errs = c.handleReplacements(replace)
if len(errs) > 0 { if len(errs) > 0 {
return errs return errs
} }
@ -2124,36 +2137,25 @@ type rename struct {
name string name string
} }
func (c *Context) rename(group *moduleGroup, name string) { func (c *Context) moduleMatchingVariant(module *moduleInfo, name string) *moduleInfo {
c.renames = append(c.renames, rename{group, name})
}
func (c *Context) replaceDependencies(module *moduleInfo, name string) {
targets := c.modulesFromName(name) targets := c.modulesFromName(name)
if targets == nil { if targets == nil {
panic(fmt.Errorf("ReplaceDependencies called with non-existant name %q", name)) return nil
} }
var target *moduleInfo
for _, m := range targets { for _, m := range targets {
if module.variantName == m.variantName { if module.variantName == m.variantName {
target = m return m
break
} }
} }
if target == nil { return nil
panic(fmt.Errorf("ReplaceDependencies could not find identical variant %q for module %q",
module.variantName, name))
}
c.replacements = append(c.replacements, replace{target, module})
} }
func (c *Context) handleRenamesAndReplacements() []error { func (c *Context) handleRenames(renames []rename) []error {
var errs []error var errs []error
for _, rename := range c.renames { for _, rename := range renames {
group, name := rename.group, rename.name group, name := rename.group, rename.name
if name == group.name { if name == group.name {
continue continue
@ -2180,7 +2182,12 @@ func (c *Context) handleRenamesAndReplacements() []error {
group.name = name group.name = name
} }
for _, replace := range c.replacements { return errs
}
func (c *Context) handleReplacements(replacements []replace) []error {
var errs []error
for _, replace := range replacements {
for _, m := range replace.from.reverseDeps { for _, m := range replace.from.reverseDeps {
for i, d := range m.directDeps { for i, d := range m.directDeps {
if d.module == replace.from { if d.module == replace.from {
@ -2191,6 +2198,7 @@ func (c *Context) handleRenamesAndReplacements() []error {
atomic.AddUint32(&c.depsModified, 1) atomic.AddUint32(&c.depsModified, 1)
} }
return errs return errs
} }

View file

@ -461,6 +461,8 @@ type mutatorContext struct {
baseModuleContext baseModuleContext
name string name string
reverseDeps []reverseDep reverseDeps []reverseDep
rename []rename
replace []replace
newModules []*moduleInfo newModules []*moduleInfo
} }
@ -669,7 +671,14 @@ func (mctx *mutatorContext) AddInterVariantDependency(tag DependencyTag, from, t
// specified name with the current variant of this module. Replacements don't take effect until // specified name with the current variant of this module. Replacements don't take effect until
// after the mutator pass is finished. // after the mutator pass is finished.
func (mctx *mutatorContext) ReplaceDependencies(name string) { func (mctx *mutatorContext) ReplaceDependencies(name string) {
mctx.context.replaceDependencies(mctx.module, name) target := mctx.context.moduleMatchingVariant(mctx.module, name)
if target == nil {
panic(fmt.Errorf("ReplaceDependencies could not find identical variant %q for module %q",
mctx.module.variantName, name))
}
mctx.replace = append(mctx.replace, replace{target, mctx.module})
} }
func (mctx *mutatorContext) OtherModuleExists(name string) bool { func (mctx *mutatorContext) OtherModuleExists(name string) bool {
@ -679,7 +688,7 @@ func (mctx *mutatorContext) OtherModuleExists(name string) bool {
// Rename all variants of a module. The new name is not visible to calls to ModuleName, // Rename all variants of a module. The new name is not visible to calls to ModuleName,
// AddDependency or OtherModuleName until after this mutator pass is complete. // AddDependency or OtherModuleName until after this mutator pass is complete.
func (mctx *mutatorContext) Rename(name string) { func (mctx *mutatorContext) Rename(name string) {
mctx.context.rename(mctx.module.group, name) mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
} }
// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property // SimpleName is an embeddable object to implement the ModuleContext.Name method using a property