2015-01-24 02:52:51 +01:00
|
|
|
package dag
|
|
|
|
|
2015-02-04 16:10:32 +01:00
|
|
|
import (
|
|
|
|
"fmt"
|
2015-02-05 01:38:38 +01:00
|
|
|
"strings"
|
2015-02-04 16:29:03 +01:00
|
|
|
"sync"
|
2015-02-07 18:52:34 +01:00
|
|
|
|
|
|
|
"github.com/hashicorp/go-multierror"
|
2015-02-04 16:10:32 +01:00
|
|
|
)
|
|
|
|
|
2015-01-24 02:52:51 +01:00
|
|
|
// AcyclicGraph is a specialization of Graph that cannot have cycles. With
|
|
|
|
// this property, we get the property of sane graph traversal.
|
|
|
|
type AcyclicGraph struct {
|
2015-02-04 16:10:32 +01:00
|
|
|
Graph
|
2015-01-24 02:52:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// WalkFunc is the callback used for walking the graph.
|
|
|
|
type WalkFunc func(Vertex)
|
|
|
|
|
2015-02-04 16:10:32 +01:00
|
|
|
// Root returns the root of the DAG, or an error.
|
|
|
|
//
|
|
|
|
// Complexity: O(V)
|
|
|
|
func (g *AcyclicGraph) Root() (Vertex, error) {
|
|
|
|
roots := make([]Vertex, 0, 1)
|
|
|
|
for _, v := range g.Vertices() {
|
|
|
|
if g.UpEdges(v).Len() == 0 {
|
|
|
|
roots = append(roots, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(roots) > 1 {
|
|
|
|
// TODO(mitchellh): make this error message a lot better
|
|
|
|
return nil, fmt.Errorf("multiple roots: %#v", roots)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(roots) == 0 {
|
|
|
|
return nil, fmt.Errorf("no roots found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return roots[0], nil
|
|
|
|
}
|
|
|
|
|
2015-02-04 16:36:33 +01:00
|
|
|
// Validate validates the DAG. A DAG is valid if it has a single root
|
|
|
|
// with no cycles.
|
|
|
|
func (g *AcyclicGraph) Validate() error {
|
|
|
|
if _, err := g.Root(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-02-07 18:52:34 +01:00
|
|
|
// Look for cycles of more than 1 component
|
|
|
|
var err error
|
2015-02-04 16:36:33 +01:00
|
|
|
var cycles [][]Vertex
|
|
|
|
for _, cycle := range StronglyConnected(&g.Graph) {
|
|
|
|
if len(cycle) > 1 {
|
|
|
|
cycles = append(cycles, cycle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(cycles) > 0 {
|
2015-02-07 18:52:34 +01:00
|
|
|
for _, cycle := range cycles {
|
2015-02-05 01:38:38 +01:00
|
|
|
cycleStr := make([]string, len(cycle))
|
|
|
|
for j, vertex := range cycle {
|
|
|
|
cycleStr[j] = VertexName(vertex)
|
|
|
|
}
|
|
|
|
|
2015-02-07 18:52:34 +01:00
|
|
|
err = multierror.Append(err, fmt.Errorf(
|
|
|
|
"Cycle: %s", strings.Join(cycleStr, ", ")))
|
2015-02-05 01:38:38 +01:00
|
|
|
}
|
2015-02-07 18:52:34 +01:00
|
|
|
}
|
2015-02-05 01:38:38 +01:00
|
|
|
|
2015-02-07 18:52:34 +01:00
|
|
|
// Look for cycles to self
|
|
|
|
for _, e := range g.Edges() {
|
|
|
|
if e.Source() == e.Target() {
|
|
|
|
err = multierror.Append(err, fmt.Errorf(
|
|
|
|
"Self reference: %s", VertexName(e.Source())))
|
|
|
|
}
|
2015-02-04 16:36:33 +01:00
|
|
|
}
|
|
|
|
|
2015-02-07 18:52:34 +01:00
|
|
|
return err
|
2015-02-04 16:36:33 +01:00
|
|
|
}
|
|
|
|
|
2015-01-24 02:52:51 +01:00
|
|
|
// Walk walks the graph, calling your callback as each node is visited.
|
2015-02-04 16:29:03 +01:00
|
|
|
// This will walk nodes in parallel if it can.
|
|
|
|
func (g *AcyclicGraph) Walk(cb WalkFunc) error {
|
2015-02-05 01:38:38 +01:00
|
|
|
// Cache the vertices since we use it multiple times
|
|
|
|
vertices := g.Vertices()
|
2015-02-04 16:29:03 +01:00
|
|
|
|
|
|
|
// Build the waitgroup that signals when we're done
|
|
|
|
var wg sync.WaitGroup
|
2015-02-05 01:38:38 +01:00
|
|
|
wg.Add(len(vertices))
|
2015-02-04 16:29:03 +01:00
|
|
|
doneCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(doneCh)
|
|
|
|
wg.Wait()
|
|
|
|
}()
|
|
|
|
|
2015-02-05 01:38:38 +01:00
|
|
|
// The map of channels to watch to wait for vertices to finish
|
|
|
|
vertMap := make(map[Vertex]chan struct{})
|
|
|
|
for _, v := range vertices {
|
|
|
|
vertMap[v] = make(chan struct{})
|
2015-02-04 16:29:03 +01:00
|
|
|
}
|
2015-02-05 01:38:38 +01:00
|
|
|
for _, v := range vertices {
|
|
|
|
// Get the list of channels to wait on
|
|
|
|
deps := g.DownEdges(v).List()
|
|
|
|
depChs := make([]<-chan struct{}, len(deps))
|
|
|
|
for i, dep := range deps {
|
|
|
|
depChs[i] = vertMap[dep.(Vertex)]
|
|
|
|
}
|
2015-02-04 16:29:03 +01:00
|
|
|
|
2015-02-05 01:38:38 +01:00
|
|
|
// Get our channel
|
|
|
|
ourCh := vertMap[v]
|
2015-02-04 16:29:03 +01:00
|
|
|
|
2015-02-05 01:38:38 +01:00
|
|
|
// Start the goroutine
|
|
|
|
go func(v Vertex, doneCh chan<- struct{}, chs []<-chan struct{}) {
|
|
|
|
defer close(doneCh)
|
|
|
|
defer wg.Done()
|
2015-02-04 16:29:03 +01:00
|
|
|
|
2015-02-05 01:38:38 +01:00
|
|
|
// Wait on all our dependencies
|
|
|
|
for _, ch := range chs {
|
|
|
|
<-ch
|
|
|
|
}
|
2015-02-04 16:29:03 +01:00
|
|
|
|
2015-02-05 01:38:38 +01:00
|
|
|
// Call our callback
|
|
|
|
cb(v)
|
|
|
|
}(v, ourCh, depChs)
|
2015-02-04 16:29:03 +01:00
|
|
|
}
|
2015-02-05 01:38:38 +01:00
|
|
|
|
|
|
|
<-doneCh
|
|
|
|
return nil
|
2015-01-24 02:52:51 +01:00
|
|
|
}
|