depgraph: if a walk func errors, don't walk dependents

This commit is contained in:
Mitchell Hashimoto 2014-07-26 18:03:18 -07:00
parent 63c0ff9260
commit e47498dc94
2 changed files with 46 additions and 10 deletions

View File

@ -259,6 +259,10 @@ func (g *Graph) Walk(fn WalkFunc) error {
seenMap := make(map[*Noun]chan struct{}) seenMap := make(map[*Noun]chan struct{})
seenMap[g.Root] = make(chan struct{}) seenMap[g.Root] = make(chan struct{})
// Keep track of what nodes errored.
var errMapL sync.RWMutex
errMap := make(map[*Noun]struct{})
// Build the list of things to visit // Build the list of things to visit
tovisit := make([]*Noun, 1, len(g.Nouns)) tovisit := make([]*Noun, 1, len(g.Nouns))
tovisit[0] = g.Root tovisit[0] = g.Root
@ -302,10 +306,23 @@ func (g *Graph) Walk(fn WalkFunc) error {
case <-quitCh: case <-quitCh:
return return
} }
// Check if any dependencies errored. If so,
// then return right away, we won't walk it.
errMapL.RLock()
_, errOk := errMap[dep.Target]
errMapL.RUnlock()
if errOk {
return
}
} }
// Call our callback! // Call our callback!
if err := fn(current); err != nil { if err := fn(current); err != nil {
errMapL.Lock()
errMap[current] = struct{}{}
errMapL.Unlock()
errCh <- err errCh <- err
} }
}(current) }(current)

View File

@ -388,21 +388,40 @@ c -> e`)
func TestGraphWalk_error(t *testing.T) { func TestGraphWalk_error(t *testing.T) {
nodes := ParseNouns(`a -> b nodes := ParseNouns(`a -> b
a -> c b -> c
b -> d a -> d`)
b -> e
c -> d
c -> e`)
list := NounMapToList(nodes) list := NounMapToList(nodes)
g := &Graph{Name: "Test", Nouns: list} g := &Graph{Name: "Test", Nouns: list}
if err := g.Validate(); err != nil { if err := g.Validate(); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
err := g.Walk(func(n *Noun) error { // We repeat this a lot because sometimes timing causes
return fmt.Errorf("foo") // a false positive.
}) for i := 0; i < 100; i++ {
if err == nil { var lock sync.Mutex
t.Fatal("should error") var walked []string
err := g.Walk(func(n *Noun) error {
lock.Lock()
defer lock.Unlock()
walked = append(walked, n.Name)
if n.Name == "b" {
return fmt.Errorf("foo")
}
return nil
})
if err == nil {
t.Fatal("should error")
}
sort.Strings(walked)
expected := []string{"b", "c", "d"}
if !reflect.DeepEqual(walked, expected) {
t.Fatalf("bad: %#v", walked)
}
} }
} }