Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Side by Side Diff: pkg/front_end/lib/src/async_dependency_walker.dart

Issue 2552383002: Add an async version of dependency_walker.dart. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6
7 /**
8 * An instance of [AsyncDependencyWalker] contains the core algorithms for
9 * walking a dependency graph and evaluating nodes in a safe order.
10 *
11 * Computation of dependencies and evaluation of nodes may be asynchronous.
12 */
13 abstract class AsyncDependencyWalker<NodeType extends Node<NodeType>> {
14 /**
15 * Called by [walk] to evaluate a single non-cyclical node, after
16 * all that node's dependencies have been evaluated.
17 */
18 Future evaluate(NodeType v);
scheglov 2016/12/06 19:24:14 Would Future<Null> be better here and below as a r
Paul Berry 2016/12/06 20:28:45 Done.
19
20 /**
21 * Called by [walk] to evaluate a strongly connected component
22 * containing one or more nodes. All dependencies of the strongly
23 * connected component have been evaluated.
24 */
25 Future evaluateScc(List<NodeType> scc);
26
27 /**
28 * Walk the dependency graph starting at [startingPoint], finding
29 * strongly connected components and evaluating them in a safe order
30 * by calling [evaluate] and [evaluateScc].
31 *
32 * This is an implementation of Tarjan's strongly connected
33 * components algorithm
34 * (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_alg orithm).
35 *
36 * TODO(paulberry): Consider switching to an algorithm that allows
37 * dependencies to be computed in parallel, and nodes to be evaluated in
38 * parallel.
39 */
40 Future walk(NodeType startingPoint) async {
41 // TODO(paulberry): consider rewriting in a non-recursive way so
42 // that long dependency chains don't cause stack overflow.
43
44 // TODO(paulberry): in the event that an exception occurs during
45 // the walk, restore the state of the [Node] data structures so
46 // that further evaluation will be safe.
47
48 // The index which will be assigned to the next node that is
49 // freshly visited.
50 int index = 1;
51
52 // Stack of nodes which have been seen so far and whose strongly
53 // connected component is still being determined. Nodes are only
54 // popped off the stack when they are evaluated, so sometimes the
55 // stack contains nodes that were visited after the current node.
56 List<NodeType> stack = <NodeType>[];
57
58 Future strongConnect(NodeType node) async {
59 bool hasTrivialCycle = false;
60
61 // Assign the current node an index and add it to the stack. We
62 // haven't seen any of its dependencies yet, so set its lowLink
63 // to its index, indicating that so far it is the only node in
64 // its strongly connected component.
65 node._index = node._lowLink = index++;
66 stack.add(node);
67
68 // Consider the node's dependencies one at a time.
69 var dependencies =
70 node._dependencies ??= await node.computeDependencies();
71 for (NodeType dependency in dependencies) {
72 // If the dependency has already been evaluated, it can't be
73 // part of this node's strongly connected component, so we can
74 // skip it.
75 if (dependency._isEvaluated) {
76 continue;
77 }
78 if (identical(node, dependency)) {
79 // If a node includes itself as a dependency, there is no need to
80 // explore the dependency further.
81 hasTrivialCycle = true;
82 } else if (dependency._index == 0) {
83 // The dependency hasn't been seen yet, so recurse on it.
84 await strongConnect(dependency);
85 // If the dependency's lowLink refers to a node that was
86 // visited before the current node, that means that the
87 // current node, the dependency, and the node referred to by
88 // the dependency's lowLink are all part of the same
89 // strongly connected component, so we need to update the
90 // current node's lowLink accordingly.
91 if (dependency._lowLink < node._lowLink) {
92 node._lowLink = dependency._lowLink;
93 }
94 } else {
95 // The dependency has already been seen, so it is part of
96 // the current node's strongly connected component. If it
97 // was visited earlier than the current node's lowLink, then
98 // it is a new addition to the current node's strongly
99 // connected component, so we need to update the current
100 // node's lowLink accordingly.
101 if (dependency._index < node._lowLink) {
102 node._lowLink = dependency._index;
103 }
104 }
105 }
106
107 // If the current node's lowLink is the same as its index, then
108 // we have finished visiting a strongly connected component, so
109 // pop the stack and evaluate it before moving on.
110 if (node._lowLink == node._index) {
111 // The strongly connected component has only one node. If there is a
112 // cycle, it's a trivial one.
113 if (identical(stack.last, node)) {
114 stack.removeLast();
115 node._isEvaluated = true;
116 if (hasTrivialCycle) {
117 await evaluateScc(<NodeType>[node]);
118 } else {
119 await evaluate(node);
120 }
121 } else {
122 // There are multiple nodes in the strongly connected
123 // component.
124 List<NodeType> scc = <NodeType>[];
125 while (true) {
126 NodeType otherNode = stack.removeLast();
127 scc.add(otherNode);
128 otherNode._isEvaluated = true;
129 if (identical(otherNode, node)) {
130 break;
131 }
132 }
133 await evaluateScc(scc);
134 }
135 }
136 }
137
138 // Kick off the algorithm starting with the starting point.
139 await strongConnect(startingPoint);
140 }
141 }
142
143 /**
144 * Instances of [Node] represent nodes in a dependency graph. The
145 * type parameter, [NodeType], is the derived type (this affords some
146 * extra type safety by making it difficult to accidentally construct
147 * bridges between unrelated dependency graphs).
148 */
149 abstract class Node<NodeType> {
150 /**
151 * Index used by Tarjan's strongly connected components algorithm.
152 * Zero means the node has not been visited yet; a nonzero value
153 * counts the order in which the node was visited.
154 */
155 int _index = 0;
156
157 /**
158 * Low link used by Tarjan's strongly connected components
159 * algorithm. This represents the smallest [_index] of all the nodes
160 * in the strongly connected component to which this node belongs.
161 */
162 int _lowLink = 0;
163
164 List<NodeType> _dependencies;
165
166 bool _isEvaluated = false;
167
168 /**
169 * Compute the dependencies of this node.
170 */
171 Future<List<NodeType>> computeDependencies();
172 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698