context: Allow running some singletons in parallel.
Many of the singletons are trivial and can be run in parallel, improving the performance during analysis. Bug: 281536768 Test: manual, presubmit Change-Id: Ia63e4bc42a68e65dfa800e770982fa5826355fad
This commit is contained in:
parent
ff04c33f2a
commit
12ccb17d4e
4 changed files with 126 additions and 44 deletions
|
@ -91,7 +91,7 @@ func RunBlueprint(args Args, stopBefore StopBefore, ctx *blueprint.Context, conf
|
|||
ctx.RegisterBottomUpMutator("bootstrap_plugin_deps", pluginDeps)
|
||||
ctx.RegisterModuleType("bootstrap_go_package", newGoPackageModuleFactory())
|
||||
ctx.RegisterModuleType("blueprint_go_binary", newGoBinaryModuleFactory())
|
||||
ctx.RegisterSingletonType("bootstrap", newSingletonFactory())
|
||||
ctx.RegisterSingletonType("bootstrap", newSingletonFactory(), false)
|
||||
blueprint.RegisterPackageIncludesModuleType(ctx)
|
||||
|
||||
ctx.BeginEvent("parse_bp")
|
||||
|
|
|
@ -227,7 +227,7 @@ func generateGlobNinjaFile(glob *GlobSingleton, config interface{}) ([]byte, []e
|
|||
ctx := blueprint.NewContext()
|
||||
ctx.RegisterSingletonType("glob", func() blueprint.Singleton {
|
||||
return glob
|
||||
})
|
||||
}, false)
|
||||
|
||||
extraDeps, errs := ctx.ResolveDependencies(config)
|
||||
if len(extraDeps) > 0 {
|
||||
|
|
164
context.go
164
context.go
|
@ -441,6 +441,7 @@ type singletonInfo struct {
|
|||
factory SingletonFactory
|
||||
singleton Singleton
|
||||
name string
|
||||
parallel bool
|
||||
|
||||
// set during PrepareBuildActions
|
||||
actionDefs localBuildActions
|
||||
|
@ -566,13 +567,15 @@ type SingletonFactory func() Singleton
|
|||
|
||||
// RegisterSingletonType registers a singleton type that will be invoked to
|
||||
// generate build actions. Each registered singleton type is instantiated
|
||||
// and invoked exactly once as part of the generate phase. Each registered
|
||||
// singleton is invoked in registration order.
|
||||
// and invoked exactly once as part of the generate phase.
|
||||
//
|
||||
// Those singletons registered with parallel=true are run in parallel, after
|
||||
// which the other registered singletons are run in registration order.
|
||||
//
|
||||
// The singleton type names given here must be unique for the context. The
|
||||
// factory function should be a named function so that its package and name can
|
||||
// be included in the generated Ninja file for debugging purposes.
|
||||
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
|
||||
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory, parallel bool) {
|
||||
for _, s := range c.singletonInfo {
|
||||
if s.name == name {
|
||||
panic(fmt.Errorf("singleton %q is already registered", name))
|
||||
|
@ -583,6 +586,7 @@ func (c *Context) RegisterSingletonType(name string, factory SingletonFactory) {
|
|||
factory: factory,
|
||||
singleton: factory(),
|
||||
name: name,
|
||||
parallel: parallel,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -605,6 +609,7 @@ func (c *Context) RegisterPreSingletonType(name string, factory SingletonFactory
|
|||
factory: factory,
|
||||
singleton: factory(),
|
||||
name: name,
|
||||
parallel: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3314,6 +3319,8 @@ func spliceModules(modules modulesOrAliases, i int, newModules modulesOrAliases)
|
|||
func (c *Context) generateModuleBuildActions(config interface{},
|
||||
liveGlobals *liveTracker) ([]string, []error) {
|
||||
|
||||
c.BeginEvent("generateModuleBuildActions")
|
||||
defer c.EndEvent("generateModuleBuildActions")
|
||||
var deps []string
|
||||
var errs []error
|
||||
|
||||
|
@ -3411,56 +3418,131 @@ func (c *Context) generateModuleBuildActions(config interface{},
|
|||
return deps, errs
|
||||
}
|
||||
|
||||
func (c *Context) generateSingletonBuildActions(config interface{},
|
||||
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||
func (c *Context) generateOneSingletonBuildActions(config interface{},
|
||||
info *singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||
|
||||
var deps []string
|
||||
var errs []error
|
||||
|
||||
for _, info := range singletons {
|
||||
// The parent scope of the singletonContext's local scope gets overridden to be that of the
|
||||
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
||||
// just set it to nil.
|
||||
scope := newLocalScope(nil, singletonNamespacePrefix(info.name))
|
||||
// The parent scope of the singletonContext's local scope gets overridden to be that of the
|
||||
// calling Go package on a per-call basis. Since the initial parent scope doesn't matter we
|
||||
// just set it to nil.
|
||||
scope := newLocalScope(nil, singletonNamespacePrefix(info.name))
|
||||
|
||||
sctx := &singletonContext{
|
||||
name: info.name,
|
||||
context: c,
|
||||
config: config,
|
||||
scope: scope,
|
||||
globals: liveGlobals,
|
||||
}
|
||||
sctx := &singletonContext{
|
||||
name: info.name,
|
||||
context: c,
|
||||
config: config,
|
||||
scope: scope,
|
||||
globals: liveGlobals,
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
in := fmt.Sprintf("GenerateBuildActions for singleton %s", info.name)
|
||||
if err, ok := r.(panicError); ok {
|
||||
err.addIn(in)
|
||||
sctx.error(err)
|
||||
} else {
|
||||
sctx.error(newPanicErrorf(r, in))
|
||||
}
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
in := fmt.Sprintf("GenerateBuildActions for singleton %s", info.name)
|
||||
if err, ok := r.(panicError); ok {
|
||||
err.addIn(in)
|
||||
sctx.error(err)
|
||||
} else {
|
||||
sctx.error(newPanicErrorf(r, in))
|
||||
}
|
||||
}()
|
||||
info.singleton.GenerateBuildActions(sctx)
|
||||
}
|
||||
}()
|
||||
info.singleton.GenerateBuildActions(sctx)
|
||||
}()
|
||||
|
||||
if len(sctx.errs) > 0 {
|
||||
errs = append(errs, sctx.errs...)
|
||||
if len(sctx.errs) > 0 {
|
||||
errs = append(errs, sctx.errs...)
|
||||
return deps, errs
|
||||
}
|
||||
|
||||
deps = append(deps, sctx.ninjaFileDeps...)
|
||||
|
||||
newErrs := c.processLocalBuildActions(&info.actionDefs,
|
||||
&sctx.actionDefs, liveGlobals)
|
||||
errs = append(errs, newErrs...)
|
||||
return deps, errs
|
||||
}
|
||||
|
||||
func (c *Context) generateParallelSingletonBuildActions(config interface{},
|
||||
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||
|
||||
c.BeginEvent("generateParallelSingletonBuildActions")
|
||||
defer c.EndEvent("generateParallelSingletonBuildActions")
|
||||
|
||||
var deps []string
|
||||
var errs []error
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
cancelCh := make(chan struct{})
|
||||
depsCh := make(chan []string)
|
||||
errsCh := make(chan []error)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-cancelCh:
|
||||
close(cancelCh)
|
||||
return
|
||||
case dep := <-depsCh:
|
||||
deps = append(deps, dep...)
|
||||
case newErrs := <-errsCh:
|
||||
if len(errs) <= maxErrors {
|
||||
errs = append(errs, newErrs...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, info := range singletons {
|
||||
if !info.parallel {
|
||||
// Skip any singletons registered with parallel=false.
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(inf *singletonInfo) {
|
||||
defer wg.Done()
|
||||
newDeps, newErrs := c.generateOneSingletonBuildActions(config, inf, liveGlobals)
|
||||
depsCh <- newDeps
|
||||
errsCh <- newErrs
|
||||
}(info)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
cancelCh <- struct{}{}
|
||||
<-cancelCh
|
||||
|
||||
return deps, errs
|
||||
}
|
||||
|
||||
func (c *Context) generateSingletonBuildActions(config interface{},
|
||||
singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {
|
||||
|
||||
c.BeginEvent("generateSingletonBuildActions")
|
||||
defer c.EndEvent("generateSingletonBuildActions")
|
||||
|
||||
var deps []string
|
||||
var errs []error
|
||||
|
||||
// Run one singleton. Use a variable to simplify manual validation testing.
|
||||
var runSingleton = func(info *singletonInfo) {
|
||||
c.BeginEvent("singleton:" + info.name)
|
||||
defer c.EndEvent("singleton:" + info.name)
|
||||
newDeps, newErrs := c.generateOneSingletonBuildActions(config, info, liveGlobals)
|
||||
deps = append(deps, newDeps...)
|
||||
errs = append(errs, newErrs...)
|
||||
}
|
||||
|
||||
// First, take care of any singletons that want to run in parallel.
|
||||
deps, errs = c.generateParallelSingletonBuildActions(config, singletons, liveGlobals)
|
||||
|
||||
for _, info := range singletons {
|
||||
if !info.parallel {
|
||||
runSingleton(info)
|
||||
if len(errs) > maxErrors {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
deps = append(deps, sctx.ninjaFileDeps...)
|
||||
|
||||
newErrs := c.processLocalBuildActions(&info.actionDefs,
|
||||
&sctx.actionDefs, liveGlobals)
|
||||
errs = append(errs, newErrs...)
|
||||
if len(errs) > maxErrors {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -590,7 +590,7 @@ func TestAddNinjaFileDeps(t *testing.T) {
|
|||
ctx.RegisterBottomUpMutator("testBottomUpMutator", addNinjaDepsTestBottomUpMutator)
|
||||
ctx.RegisterTopDownMutator("testTopDownMutator", addNinjaDepsTestTopDownMutator)
|
||||
ctx.RegisterPreSingletonType("testPreSingleton", addNinjaDepsTestPreSingletonFactory)
|
||||
ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory)
|
||||
ctx.RegisterSingletonType("testSingleton", addNinjaDepsTestSingletonFactory, false)
|
||||
parseDeps, errs := ctx.ParseBlueprintsFiles("Android.bp", nil)
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("unexpected parse errors:")
|
||||
|
|
Loading…
Reference in a new issue