github.com/jefferai/terraform@v0.3.7-0.20150310153852-f7512ca29fcf/dag/dag.go (about)

     1  package dag
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/hashicorp/go-multierror"
     9  )
    10  
    11  // AcyclicGraph is a specialization of Graph that cannot have cycles. With
    12  // this property, we get the property of sane graph traversal.
    13  type AcyclicGraph struct {
    14  	Graph
    15  }
    16  
    17  // WalkFunc is the callback used for walking the graph.
    18  type WalkFunc func(Vertex) error
    19  
    20  // Root returns the root of the DAG, or an error.
    21  //
    22  // Complexity: O(V)
    23  func (g *AcyclicGraph) Root() (Vertex, error) {
    24  	roots := make([]Vertex, 0, 1)
    25  	for _, v := range g.Vertices() {
    26  		if g.UpEdges(v).Len() == 0 {
    27  			roots = append(roots, v)
    28  		}
    29  	}
    30  
    31  	if len(roots) > 1 {
    32  		// TODO(mitchellh): make this error message a lot better
    33  		return nil, fmt.Errorf("multiple roots: %#v", roots)
    34  	}
    35  
    36  	if len(roots) == 0 {
    37  		return nil, fmt.Errorf("no roots found")
    38  	}
    39  
    40  	return roots[0], nil
    41  }
    42  
    43  // TransitiveReduction performs the transitive reduction of graph g in place.
    44  // The transitive reduction of a graph is a graph with as few edges as
    45  // possible with the same reachability as the original graph. This means
    46  // that if there are three nodes A => B => C, and A connects to both
    47  // B and C, and B connects to C, then the transitive reduction is the
    48  // same graph with only a single edge between A and B, and a single edge
    49  // between B and C.
    50  //
    51  // The graph must be valid for this operation to behave properly. If
    52  // Validate() returns an error, the behavior is undefined and the results
    53  // will likely be unexpected.
    54  //
    55  // Complexity: O(V(V+E)), or asymptotically O(VE)
    56  func (g *AcyclicGraph) TransitiveReduction() {
    57  	// For each vertex u in graph g, do a DFS starting from each vertex
    58  	// v such that the edge (u,v) exists (v is a direct descendant of u).
    59  	//
    60  	// For each v-prime reachable from v, remove the edge (u, v-prime).
    61  
    62  	for _, u := range g.Vertices() {
    63  		uTargets := g.DownEdges(u)
    64  		vs := make([]Vertex, uTargets.Len())
    65  		for i, vRaw := range uTargets.List() {
    66  			vs[i] = vRaw.(Vertex)
    67  		}
    68  
    69  		g.depthFirstWalk(vs, func(v Vertex) error {
    70  			shared := uTargets.Intersection(g.DownEdges(v))
    71  			for _, raw := range shared.List() {
    72  				vPrime := raw.(Vertex)
    73  				g.RemoveEdge(BasicEdge(u, vPrime))
    74  			}
    75  
    76  			return nil
    77  		})
    78  	}
    79  }
    80  
    81  // Validate validates the DAG. A DAG is valid if it has a single root
    82  // with no cycles.
    83  func (g *AcyclicGraph) Validate() error {
    84  	if _, err := g.Root(); err != nil {
    85  		return err
    86  	}
    87  
    88  	// Look for cycles of more than 1 component
    89  	var err error
    90  	var cycles [][]Vertex
    91  	for _, cycle := range StronglyConnected(&g.Graph) {
    92  		if len(cycle) > 1 {
    93  			cycles = append(cycles, cycle)
    94  		}
    95  	}
    96  	if len(cycles) > 0 {
    97  		for _, cycle := range cycles {
    98  			cycleStr := make([]string, len(cycle))
    99  			for j, vertex := range cycle {
   100  				cycleStr[j] = VertexName(vertex)
   101  			}
   102  
   103  			err = multierror.Append(err, fmt.Errorf(
   104  				"Cycle: %s", strings.Join(cycleStr, ", ")))
   105  		}
   106  	}
   107  
   108  	// Look for cycles to self
   109  	for _, e := range g.Edges() {
   110  		if e.Source() == e.Target() {
   111  			err = multierror.Append(err, fmt.Errorf(
   112  				"Self reference: %s", VertexName(e.Source())))
   113  		}
   114  	}
   115  
   116  	return err
   117  }
   118  
   119  // Walk walks the graph, calling your callback as each node is visited.
   120  // This will walk nodes in parallel if it can. Because the walk is done
   121  // in parallel, the error returned will be a multierror.
   122  func (g *AcyclicGraph) Walk(cb WalkFunc) error {
   123  	// Cache the vertices since we use it multiple times
   124  	vertices := g.Vertices()
   125  
   126  	// Build the waitgroup that signals when we're done
   127  	var wg sync.WaitGroup
   128  	wg.Add(len(vertices))
   129  	doneCh := make(chan struct{})
   130  	go func() {
   131  		defer close(doneCh)
   132  		wg.Wait()
   133  	}()
   134  
   135  	// The map of channels to watch to wait for vertices to finish
   136  	vertMap := make(map[Vertex]chan struct{})
   137  	for _, v := range vertices {
   138  		vertMap[v] = make(chan struct{})
   139  	}
   140  
   141  	// The map of whether a vertex errored or not during the walk
   142  	var errLock sync.Mutex
   143  	var errs error
   144  	errMap := make(map[Vertex]bool)
   145  	for _, v := range vertices {
   146  		// Build our list of dependencies and the list of channels to
   147  		// wait on until we start executing for this vertex.
   148  		depsRaw := g.DownEdges(v).List()
   149  		deps := make([]Vertex, len(depsRaw))
   150  		depChs := make([]<-chan struct{}, len(deps))
   151  		for i, raw := range depsRaw {
   152  			deps[i] = raw.(Vertex)
   153  			depChs[i] = vertMap[deps[i]]
   154  		}
   155  
   156  		// Get our channel so that we can close it when we're done
   157  		ourCh := vertMap[v]
   158  
   159  		// Start the goroutine to wait for our dependencies
   160  		readyCh := make(chan bool)
   161  		go func(deps []Vertex, chs []<-chan struct{}, readyCh chan<- bool) {
   162  			// First wait for all the dependencies
   163  			for _, ch := range chs {
   164  				<-ch
   165  			}
   166  
   167  			// Then, check the map to see if any of our dependencies failed
   168  			errLock.Lock()
   169  			defer errLock.Unlock()
   170  			for _, dep := range deps {
   171  				if errMap[dep] {
   172  					readyCh <- false
   173  					return
   174  				}
   175  			}
   176  
   177  			readyCh <- true
   178  		}(deps, depChs, readyCh)
   179  
   180  		// Start the goroutine that executes
   181  		go func(v Vertex, doneCh chan<- struct{}, readyCh <-chan bool) {
   182  			defer close(doneCh)
   183  			defer wg.Done()
   184  
   185  			var err error
   186  			if ready := <-readyCh; ready {
   187  				err = cb(v)
   188  			}
   189  
   190  			errLock.Lock()
   191  			defer errLock.Unlock()
   192  			if err != nil {
   193  				errMap[v] = true
   194  				errs = multierror.Append(errs, err)
   195  			}
   196  		}(v, ourCh, readyCh)
   197  	}
   198  
   199  	<-doneCh
   200  	return errs
   201  }
   202  
   203  // depthFirstWalk does a depth-first walk of the graph starting from
   204  // the vertices in start. This is not exported now but it would make sense
   205  // to export this publicly at some point.
   206  func (g *AcyclicGraph) depthFirstWalk(start []Vertex, cb WalkFunc) error {
   207  	seen := make(map[Vertex]struct{})
   208  	frontier := make([]Vertex, len(start))
   209  	copy(frontier, start)
   210  	for len(frontier) > 0 {
   211  		// Pop the current vertex
   212  		n := len(frontier)
   213  		current := frontier[n-1]
   214  		frontier = frontier[:n-1]
   215  
   216  		// Check if we've seen this already and return...
   217  		if _, ok := seen[current]; ok {
   218  			continue
   219  		}
   220  		seen[current] = struct{}{}
   221  
   222  		// Visit the current node
   223  		if err := cb(current); err != nil {
   224  			return err
   225  		}
   226  
   227  		// Visit targets of this in reverse order.
   228  		targets := g.DownEdges(current).List()
   229  		for i := len(targets) - 1; i >= 0; i-- {
   230  			frontier = append(frontier, targets[i].(Vertex))
   231  		}
   232  	}
   233  
   234  	return nil
   235  }