diff --git a/dag/walk.go b/dag/walk.go index 12c421681..eb49b0c41 100644 --- a/dag/walk.go +++ b/dag/walk.go @@ -9,17 +9,30 @@ import ( "github.com/hashicorp/go-multierror" ) -// walker performs a graph walk +// walker performs a graph walk and supports walk-time changing of vertices +// and edges. +// +// A single walker is only valid for one graph walk. After the walk is complete +// you must construct a new walker to walk again. State for the walk is never +// deleted in case vertices or edges are changed. type walker struct { + // Callback is what is called for each vertex Callback WalkFunc - vertices Set - edges Set - vertexMap map[Vertex]*walkerVertex - - wait sync.WaitGroup + // changeLock must be held to modify any of the fields below. Only Update + // should modify these fields. Modifying them outside of Update can cause + // serious problems. changeLock sync.Mutex + vertices Set + edges Set + vertexMap map[Vertex]*walkerVertex + // wait is done when all vertices have executed. It may become "undone" + // if new vertices are added. + wait sync.WaitGroup + + // errMap contains the errors recorded so far for execution. Reading + // and writing should hold errLock. errMap map[Vertex]error errLock sync.Mutex } @@ -43,7 +56,11 @@ type walkerVertex struct { // Wait waits for the completion of the walk and returns any errors ( // in the form of a multierror) that occurred. Update should be called -// to populate the walk with vertices and edges. +// to populate the walk with vertices and edges prior to calling this. +// +// Wait will return as soon as all currently known vertices are complete. +// If you plan on calling Update with more vertices in the future, you +// should not call Wait until after this is done. func (w *walker) Wait() error { // Wait for completion w.wait.Wait() @@ -272,7 +289,10 @@ func (w *walker) walkVertex(v Vertex, info *walkerVertex) { // New deps, reloop } - // Check if we have updated dependencies + // Check if we have updated dependencies. This can happen if the + // dependencies were satisfied exactly prior to an Update occuring. + // In that case, we'd like to take into account new dependencies + // if possible. info.DepsLock.Lock() if info.DepsCh != nil { depsCh = info.DepsCh