summaryrefslogtreecommitdiffstats
path: root/lazy/init.go
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-04-29 19:05:28 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-05-01 16:00:31 +0200
commit009076e5ee88fc46c95a9afd34f82f9386aa282a (patch)
treed31760ca930ce6f786ac9ed1e99f515f68d73e71 /lazy/init.go
parent1cbb501be8aa83b08865fbb6ad5aee254946712f (diff)
lazy: Fix concurrent initialization order
Fixes #5901
Diffstat (limited to 'lazy/init.go')
-rw-r--r--lazy/init.go79
1 files changed, 39 insertions, 40 deletions
diff --git a/lazy/init.go b/lazy/init.go
index 5c1bee609..a54fda96a 100644
--- a/lazy/init.go
+++ b/lazy/init.go
@@ -77,30 +77,19 @@ func (ini *Init) Do() (interface{}, error) {
}
ini.init.Do(func() {
- var (
- dependencies []*Init
- children []*Init
- )
-
prev := ini.prev
- for prev != nil {
+ if prev != nil {
+ // A branch. Initialize the ancestors.
if prev.shouldInitialize() {
- dependencies = append(dependencies, prev)
- }
- prev = prev.prev
- }
-
- for _, child := range ini.children {
- if child.shouldInitialize() {
- children = append(children, child)
- }
- }
-
- for _, dep := range dependencies {
- _, err := dep.Do()
- if err != nil {
- ini.err = err
- return
+ _, err := prev.Do()
+ if err != nil {
+ ini.err = err
+ return
+ }
+ } else if prev.inProgress() {
+ // Concurrent initialization. The following init func
+ // may depend on earlier state, so wait.
+ prev.wait()
}
}
@@ -108,16 +97,25 @@ func (ini *Init) Do() (interface{}, error) {
ini.out, ini.err = ini.f()
}
- for _, dep := range children {
- _, err := dep.Do()
- if err != nil {
- ini.err = err
- return
+ for _, child := range ini.children {
+ if child.shouldInitialize() {
+ _, err := child.Do()
+ if err != nil {
+ ini.err = err
+ return
+ }
}
}
-
})
+ ini.wait()
+
+ return ini.out, ini.err
+
+}
+
+// TODO(bep) investigate if we can use sync.Cond for this.
+func (ini *Init) wait() {
var counter time.Duration
for !ini.init.Done() {
counter += 10
@@ -126,8 +124,10 @@ func (ini *Init) Do() (interface{}, error) {
}
time.Sleep(counter * time.Microsecond)
}
+}
- return ini.out, ini.err
+func (ini *Init) inProgress() bool {
+ return ini != nil && ini.init.InProgress()
}
func (ini *Init) shouldInitialize() bool {
@@ -147,20 +147,19 @@ func (ini *Init) add(branch bool, initFn func() (interface{}, error)) *Init {
ini.mu.Lock()
defer ini.mu.Unlock()
- if !branch {
- ini.checkDone()
- }
-
- init := &Init{
- f: initFn,
- prev: ini,
+ if branch {
+ return &Init{
+ f: initFn,
+ prev: ini,
+ }
}
- if !branch {
- ini.children = append(ini.children, init)
- }
+ ini.checkDone()
+ ini.children = append(ini.children, &Init{
+ f: initFn,
+ })
- return init
+ return ini
}
func (ini *Init) checkDone() {