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

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: code review 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 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
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.
97 bool get isDirty => _inputs.values.any((input) => input.isDirty) || 97 ///
98 /// A phase is considered dirty if any of the previous phases in the same
99 /// cascade are dirty, since those phases could emit an asset that this phase
100 /// will then need to process.
101 bool get isDirty => (_previous != null && _previous.isDirty) ||
102 _inputs.values.any((input) => input.isDirty) ||
98 _groups.values.any((group) => group.isDirty); 103 _groups.values.any((group) => group.isDirty);
99 104
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 105 /// A stream that emits an event whenever any transforms in this phase logs
105 /// an entry. 106 /// an entry.
106 Stream<LogEntry> get onLog => _onLogPool.stream; 107 Stream<LogEntry> get onLog => _onLogPool.stream;
107 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 108 final _onLogPool = new StreamPool<LogEntry>.broadcast();
108 109
109 /// The previous phase in the cascade, or null if this is the first phase. 110 /// The previous phase in the cascade, or null if this is the first phase.
110 final Phase _previous; 111 final Phase _previous;
111 112
113 /// The subscription to [_previous]'s [onDone] stream.
114 StreamSubscription _previousOnDoneSubscription;
115
112 /// The phase after this one. 116 /// The phase after this one.
113 /// 117 ///
114 /// Outputs from this phase will be passed to it. 118 /// Outputs from this phase will be passed to it.
115 Phase get next => _next; 119 Phase get next => _next;
116 Phase _next; 120 Phase _next;
117 121
118 /// A map of asset ids to completers for [getInput] requests. 122 /// A map of asset ids to completers for [getInput] requests.
119 /// 123 ///
120 /// If an asset node is requested before it's available, we put a completer in 124 /// 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 125 /// this map to wait for the asset to be generated. If it's not generated, the
122 /// completer should complete to `null`. 126 /// completer should complete to `null`.
123 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); 127 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>();
124 128
125 /// Returns all currently-available output assets for this phase. 129 /// Returns all currently-available output assets for this phase.
126 Set<AssetNode> get availableOutputs { 130 Set<AssetNode> get availableOutputs {
127 return _outputs.values 131 return _outputs.values
128 .map((output) => output.output) 132 .map((output) => output.output)
129 .where((node) => node.state.isAvailable) 133 .where((node) => node.state.isAvailable)
130 .toSet(); 134 .toSet();
131 } 135 }
132 136
133 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, 137 // TODO(nweiz): Rather than passing the cascade and the phase everywhere,
134 // create an interface that just exposes [getInput]. Emit errors via 138 // create an interface that just exposes [getInput]. Emit errors via
135 // [AssetNode]s. 139 // [AssetNode]s.
136 Phase(AssetCascade cascade, String location) 140 Phase(AssetCascade cascade, String location)
137 : this._(cascade, location, 0); 141 : this._(cascade, location, 0);
138 142
139 Phase._(this.cascade, this._location, this._index, [this._previous]) { 143 Phase._(this.cascade, this._location, this._index, [this._previous]) {
140 // TODO(nweiz): This does O(n^2) work whenever a phase emits an [onDone] 144 if (_previous != null) {
141 // event, since each phase after it has to check each phase before. Find a 145 _previousOnDoneSubscription = _previous.onDone.listen((_) {
142 // better way to do this. 146 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 }); 147 });
155 } 148 }
149
150 onDone.listen((_) {
151 // All the previous phases have finished building. If anyone's still
152 // waiting for outputs, cut off the wait; we won't be generating them,
153 // at least until a source asset changes.
154 for (var completer in _pendingOutputRequests.values) {
155 completer.complete(null);
156 }
157 _pendingOutputRequests.clear();
158 });
156 } 159 }
157 160
158 /// Adds a new asset as an input for this phase. 161 /// Adds a new asset as an input for this phase.
159 /// 162 ///
160 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase 163 /// [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 164 /// will automatically begin determining which transforms can consume it as a
162 /// primary input. The transforms themselves won't be applied until [process] 165 /// primary input. The transforms themselves won't be applied until [process]
163 /// is called, however. 166 /// is called, however.
164 /// 167 ///
165 /// This should only be used for brand-new assets or assets that have been 168 /// 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. 246 // try again, since it could be generated again.
244 output.force(); 247 output.force();
245 return output.whenAvailable((_) => output).catchError((error) { 248 return output.whenAvailable((_) => output).catchError((error) {
246 if (error is! AssetNotFoundException) throw error; 249 if (error is! AssetNotFoundException) throw error;
247 return getOutput(id); 250 return getOutput(id);
248 }); 251 });
249 } 252 }
250 253
251 // If neither this phase nor the previous phases are dirty, the requested 254 // If neither this phase nor the previous phases are dirty, the requested
252 // output won't be generated and we can safely return null. 255 // output won't be generated and we can safely return null.
253 if (!_isTransitivelyDirty) return null; 256 if (!isDirty) return null;
254 257
255 // Otherwise, store a completer for the asset node. If it's generated in 258 // Otherwise, store a completer for the asset node. If it's generated in
256 // the future, we'll complete this completer. 259 // the future, we'll complete this completer.
257 var completer = _pendingOutputRequests.putIfAbsent(id, 260 var completer = _pendingOutputRequests.putIfAbsent(id,
258 () => new Completer.sync()); 261 () => new Completer.sync());
259 return completer.future; 262 return completer.future;
260 }); 263 });
261 } 264 }
262 265
263 /// Set this phase's transformers to [transformers]. 266 /// Set this phase's transformers to [transformers].
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
328 if (_previous != null) _previous._next = null; 331 if (_previous != null) _previous._next = null;
329 removeFollowing(); 332 removeFollowing();
330 for (var input in _inputs.values.toList()) { 333 for (var input in _inputs.values.toList()) {
331 input.remove(); 334 input.remove();
332 } 335 }
333 for (var group in _groups.values) { 336 for (var group in _groups.values) {
334 group.remove(); 337 group.remove();
335 } 338 }
336 _onAssetController.close(); 339 _onAssetController.close();
337 _onLogPool.close(); 340 _onLogPool.close();
341 _previousOnDoneSubscription.cancel();
338 } 342 }
339 343
340 /// Remove all phases after this one. 344 /// Remove all phases after this one.
341 void removeFollowing() { 345 void removeFollowing() {
342 if (_next == null) return; 346 if (_next == null) return;
343 _next.remove(); 347 _next.remove();
344 _next = null; 348 _next = null;
345 } 349 }
346 350
347 /// Add [asset] as an output of this phase. 351 /// 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); 402 assert(asset.state.isDirty);
399 asset.force(); 403 asset.force();
400 asset.whenStateChanges().then((state) { 404 asset.whenStateChanges().then((state) {
401 if (state.isRemoved) return getOutput(asset.id); 405 if (state.isRemoved) return getOutput(asset.id);
402 return asset; 406 return asset;
403 }).then(request.complete).catchError(request.completeError); 407 }).then(request.complete).catchError(request.completeError);
404 } 408 }
405 409
406 String toString() => "phase $_location.$_index"; 410 String toString() => "phase $_location.$_index";
407 } 411 }
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