Add generic system for shared resource locks to maker
This should fix concurrent access to go packages causing problems on
windows.
Change-Id: Ic43c4dcc301269abbcc2577d1e351bbf0d788250
diff --git a/make.go b/make.go
index 36c209c..5551337 100644
--- a/make.go
+++ b/make.go
@@ -186,5 +186,5 @@
}
func Codergen(name string, args ...string) {
- Command(Tools.Codergen, args...).Creates(Virtual(name)).DependsOn("rpcapi", "apic")
+ Command(Tools.Codergen, args...).Creates(Virtual(name)).DependsOn("rpcapi", "apic").Access(GoPkgResources)
}
diff --git a/maker/access.go b/maker/access.go
new file mode 100644
index 0000000..92d196a
--- /dev/null
+++ b/maker/access.go
@@ -0,0 +1,48 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package maker
+
+import (
+ "sort"
+ "sync"
+)
+
+type lock struct {
+ name string
+ mu sync.Mutex
+}
+
+var locks = map[string]*lock{}
+
+func addLock(name string) {
+ _, ok := locks[name]
+ if !ok {
+ locks[name] = &lock{name: name}
+ }
+}
+
+func withLocks(accesses []string, do func()) {
+ // sort the names for consistent aquire order
+ sort.Strings(accesses)
+ // aquire all the mutexes in order
+ for _, name := range accesses {
+ locks[name].mu.Lock()
+ }
+ do()
+ // unlock order does not matter
+ for _, name := range accesses {
+ locks[name].mu.Unlock()
+ }
+}
diff --git a/maker/go.go b/maker/go.go
index c263655..54c8dbf 100644
--- a/maker/go.go
+++ b/maker/go.go
@@ -14,15 +14,11 @@
package maker
-import "sync"
-
-var golock sync.Mutex
+const GoPkgResources = "go_packages"
// GoCommand runs "go" with the specified arguments.
func GoCommand(args ...string) *Step {
- golock.Lock()
- defer golock.Unlock()
- return Command(goTool, args...)
+ return Command(goTool, args...).Access(GoPkgResources)
}
// GoInstall builds a new Step that runs "go install" on the supplied module.
diff --git a/maker/step.go b/maker/step.go
index 42e9516..00c2d42 100644
--- a/maker/step.go
+++ b/maker/step.go
@@ -30,13 +30,14 @@
// and may depend on many entities.
// It is an error to have a cycle in the build graph.
type Step struct {
- inputs []Entity
- outputs []Entity
- always bool
- action func(*Step) error
- once sync.Once
- done chan struct{}
- err error
+ inputs []Entity
+ outputs []Entity
+ always bool
+ action func(*Step) error
+ once sync.Once
+ done chan struct{}
+ err error
+ accesses []string
}
var (
@@ -64,6 +65,9 @@
action: a,
done: make(chan struct{}),
}
+ if Config.DisableParallel {
+ s.Access("single_thread")
+ }
for _, h := range stepHooks {
h(s)
}
@@ -101,6 +105,16 @@
return s
}
+// Access adds the supplied shared resource names to the list of resources
+// accessed by this step.
+func (s *Step) Access(v ...string) *Step {
+ s.accesses = append(s.accesses, v...)
+ for _, name := range v {
+ addLock(name)
+ }
+ return s
+}
+
// HasInput returns true if the step already has the supplied entity in it's inputs.
func (s *Step) HasInput(e Entity) bool {
for _, in := range s.inputs {
@@ -181,11 +195,7 @@
dep := Creator(e)
if dep != nil {
deps = append(deps, dep)
- if Config.DisableParallel {
- dep.start()
- } else {
- go dep.start()
- }
+ go dep.start()
}
}
// Wait for all inputs to be ready
@@ -236,7 +246,7 @@
s.once.Do(func() {
s.updateInputs()
if s.err == nil && s.shouldRun() {
- s.run()
+ withLocks(s.accesses, s.run)
}
// Signal we are complete
close(s.done)