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

Side by Side Diff: pkg/barback/lib/src/phase.dart

Issue 189623006: Avoid O(n^2) behavior in Barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 months 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 | Annotate | Revision Log
« no previous file with comments | « pkg/barback/lib/src/group_runner.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library barback.phase; 5 library barback.phase;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'asset_cascade.dart'; 9 import 'asset_cascade.dart';
10 import 'asset_id.dart'; 10 import 'asset_id.dart';
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 final _onDoneController = new StreamController.broadcast(sync: true); 86 final _onDoneController = new StreamController.broadcast(sync: true);
87 87
88 /// A stream that emits any new assets emitted by [this]. 88 /// A stream that emits any new assets emitted by [this].
89 /// 89 ///
90 /// Assets are emitted synchronously to ensure that any changes are thoroughly 90 /// Assets are emitted synchronously to ensure that any changes are thoroughly
91 /// propagated as soon as they occur. Only a phase with no [next] phase will 91 /// propagated as soon as they occur. Only a phase with no [next] phase will
92 /// emit assets. 92 /// emit assets.
93 Stream<AssetNode> get onAsset => _onAssetController.stream; 93 Stream<AssetNode> get onAsset => _onAssetController.stream;
94 final _onAssetController = new StreamController<AssetNode>(sync: true); 94 final _onAssetController = new StreamController<AssetNode>(sync: true);
95 95
96 /// Whether [this] is dirty and still has more processing to do. 96 /// Whether [this] is dirty and still has more processing to do.
Bob Nystrom 2014/03/07 17:05:58 Add a bit here explaining that a phase is if it's
nweiz 2014/03/07 19:39:58 Done.
97 bool get isDirty => _inputs.values.any((input) => input.isDirty) || 97 bool get isDirty => (_previous != null && _previous.isDirty) ||
98 _inputs.values.any((input) => input.isDirty) ||
98 _groups.values.any((group) => group.isDirty); 99 _groups.values.any((group) => group.isDirty);
99 100
100 /// Whether [this] or any previous phase is dirty.
101 bool get _isTransitivelyDirty => isDirty ||
102 (_previous != null && _previous._isTransitivelyDirty);
103
104 /// A stream that emits an event whenever any transforms in this phase logs 101 /// A stream that emits an event whenever any transforms in this phase logs
105 /// an entry. 102 /// an entry.
106 Stream<LogEntry> get onLog => _onLogPool.stream; 103 Stream<LogEntry> get onLog => _onLogPool.stream;
107 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 104 final _onLogPool = new StreamPool<LogEntry>.broadcast();
108 105
109 /// The previous phase in the cascade, or null if this is the first phase. 106 /// The previous phase in the cascade, or null if this is the first phase.
110 final Phase _previous; 107 final Phase _previous;
111 108
109 /// The subscription to [_previous]'s [onDone] stream.
110 StreamSubscription _previousSubscription;
Bob Nystrom 2014/03/07 17:05:58 _previousOnDoneSubscription?
nweiz 2014/03/07 19:39:58 Done.
111
112 /// The phase after this one. 112 /// The phase after this one.
113 /// 113 ///
114 /// Outputs from this phase will be passed to it. 114 /// Outputs from this phase will be passed to it.
115 Phase get next => _next; 115 Phase get next => _next;
116 Phase _next; 116 Phase _next;
117 117
118 /// A map of asset ids to completers for [getInput] requests. 118 /// A map of asset ids to completers for [getInput] requests.
119 /// 119 ///
120 /// If an asset node is requested before it's available, we put a completer in 120 /// If an asset node is requested before it's available, we put a completer in
121 /// this map to wait for the asset to be generated. If it's not generated, the 121 /// this map to wait for the asset to be generated. If it's not generated, the
122 /// completer should complete to `null`. 122 /// completer should complete to `null`.
123 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); 123 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>();
124 124
125 /// Returns all currently-available output assets for this phase. 125 /// Returns all currently-available output assets for this phase.
126 Set<AssetNode> get availableOutputs { 126 Set<AssetNode> get availableOutputs {
127 return _outputs.values 127 return _outputs.values
128 .map((output) => output.output) 128 .map((output) => output.output)
129 .where((node) => node.state.isAvailable) 129 .where((node) => node.state.isAvailable)
130 .toSet(); 130 .toSet();
131 } 131 }
132 132
133 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, 133 // TODO(nweiz): Rather than passing the cascade and the phase everywhere,
134 // create an interface that just exposes [getInput]. Emit errors via 134 // create an interface that just exposes [getInput]. Emit errors via
135 // [AssetNode]s. 135 // [AssetNode]s.
136 Phase(AssetCascade cascade, String location) 136 Phase(AssetCascade cascade, String location)
137 : this._(cascade, location, 0); 137 : this._(cascade, location, 0);
138 138
139 Phase._(this.cascade, this._location, this._index, [this._previous]) { 139 Phase._(this.cascade, this._location, this._index, [this._previous]) {
140 // TODO(nweiz): This does O(n^2) work whenever a phase emits an [onDone] 140 if (_previous != null) {
141 // event, since each phase after it has to check each phase before. Find a 141 _previousSubscription = _previous.onDone.listen((_) {
142 // better way to do this. 142 if (!isDirty) _onDoneController.add(null);
143 for (var phase = this; phase != null; phase = phase._previous) {
144 phase.onDone.listen((_) {
145 if (_isTransitivelyDirty) return;
146
147 // All the previous phases have finished building. If anyone's still
148 // waiting for outputs, cut off the wait; we won't be generating them,
149 // at least until a source asset changes.
150 for (var completer in _pendingOutputRequests.values) {
151 completer.complete(null);
152 }
153 _pendingOutputRequests.clear();
154 }); 143 });
155 } 144 }
145
146 this.onDone.listen((_) {
Bob Nystrom 2014/03/07 17:05:58 Remove "this.".
nweiz 2014/03/07 19:39:58 Done.
147 // All the previous phases have finished building. If anyone's still
148 // waiting for outputs, cut off the wait; we won't be generating them,
149 // at least until a source asset changes.
150 for (var completer in _pendingOutputRequests.values) {
151 completer.complete(null);
152 }
153 _pendingOutputRequests.clear();
154 });
156 } 155 }
157 156
158 /// Adds a new asset as an input for this phase. 157 /// Adds a new asset as an input for this phase.
159 /// 158 ///
160 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 159 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase
161 /// will automatically begin determining which transforms can consume it as a 160 /// will automatically begin determining which transforms can consume it as a
162 /// primary input. The transforms themselves won't be applied until [process] 161 /// primary input. The transforms themselves won't be applied until [process]
163 /// is called, however. 162 /// is called, however.
164 /// 163 ///
165 /// This should only be used for brand-new assets or assets that have been 164 /// This should only be used for brand-new assets or assets that have been
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
243 // try again, since it could be generated again. 242 // try again, since it could be generated again.
244 output.force(); 243 output.force();
245 return output.whenAvailable((_) => output).catchError((error) { 244 return output.whenAvailable((_) => output).catchError((error) {
246 if (error is! AssetNotFoundException) throw error; 245 if (error is! AssetNotFoundException) throw error;
247 return getOutput(id); 246 return getOutput(id);
248 }); 247 });
249 } 248 }
250 249
251 // If neither this phase nor the previous phases are dirty, the requested 250 // If neither this phase nor the previous phases are dirty, the requested
252 // output won't be generated and we can safely return null. 251 // output won't be generated and we can safely return null.
253 if (!_isTransitivelyDirty) return null; 252 if (!isDirty) return null;
254 253
255 // Otherwise, store a completer for the asset node. If it's generated in 254 // Otherwise, store a completer for the asset node. If it's generated in
256 // the future, we'll complete this completer. 255 // the future, we'll complete this completer.
257 var completer = _pendingOutputRequests.putIfAbsent(id, 256 var completer = _pendingOutputRequests.putIfAbsent(id,
258 () => new Completer.sync()); 257 () => new Completer.sync());
259 return completer.future; 258 return completer.future;
260 }); 259 });
261 } 260 }
262 261
263 /// Set this phase's transformers to [transformers]. 262 /// Set this phase's transformers to [transformers].
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 _previous._next = null; 327 _previous._next = null;
329 removeFollowing(); 328 removeFollowing();
330 for (var input in _inputs.values.toList()) { 329 for (var input in _inputs.values.toList()) {
331 input.remove(); 330 input.remove();
332 } 331 }
333 for (var group in _groups.values) { 332 for (var group in _groups.values) {
334 group.remove(); 333 group.remove();
335 } 334 }
336 _onAssetController.close(); 335 _onAssetController.close();
337 _onLogPool.close(); 336 _onLogPool.close();
337 _previousSubscription.cancel();
338 } 338 }
339 339
340 /// Remove all phases after this one. 340 /// Remove all phases after this one.
341 void removeFollowing() { 341 void removeFollowing() {
342 if (_next == null) return; 342 if (_next == null) return;
343 _next.remove(); 343 _next.remove();
344 _next = null; 344 _next = null;
345 } 345 }
346 346
347 /// Add [asset] as an output of this phase. 347 /// Add [asset] as an output of this phase.
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 assert(asset.state.isDirty); 398 assert(asset.state.isDirty);
399 asset.force(); 399 asset.force();
400 asset.whenStateChanges().then((state) { 400 asset.whenStateChanges().then((state) {
401 if (state.isRemoved) return getOutput(asset.id); 401 if (state.isRemoved) return getOutput(asset.id);
402 return asset; 402 return asset;
403 }).then(request.complete).catchError(request.completeError); 403 }).then(request.complete).catchError(request.completeError);
404 } 404 }
405 405
406 String toString() => "phase $_location.$_index"; 406 String toString() => "phase $_location.$_index";
407 } 407 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/group_runner.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698