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 }