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

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

Issue 233843002: Don't make lazy transformers eager when an asset is requested. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 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 | « no previous file | pkg/barback/test/package_graph/lazy_transformer_test.dart » ('j') | 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.transform_node; 5 library barback.transform_node;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'asset.dart'; 9 import 'asset.dart';
10 import 'asset_id.dart'; 10 import 'asset_id.dart';
(...skipping 27 matching lines...) Expand all
38 /// A string describing the location of [this] in the transformer graph. 38 /// A string describing the location of [this] in the transformer graph.
39 final String _location; 39 final String _location;
40 40
41 /// The subscription to [primary]'s [AssetNode.onStateChange] stream. 41 /// The subscription to [primary]'s [AssetNode.onStateChange] stream.
42 StreamSubscription _primarySubscription; 42 StreamSubscription _primarySubscription;
43 43
44 /// The subscription to [phase]'s [Phase.onAsset] stream. 44 /// The subscription to [phase]'s [Phase.onAsset] stream.
45 StreamSubscription<AssetNode> _phaseSubscription; 45 StreamSubscription<AssetNode> _phaseSubscription;
46 46
47 /// Whether [this] is dirty and still has more processing to do. 47 /// Whether [this] is dirty and still has more processing to do.
48 bool get isDirty => _state != _State.NOT_PRIMARY && _state != _State.APPLIED; 48 bool get isDirty => _state != _State.NOT_PRIMARY &&
49 _state != _State.APPLIED && _state != _State.DECLARED;
49 50
50 /// Whether this transform is lazy and this transform has yet to be forced. 51 /// Whether this transform is lazy.
51 /// 52 ///
52 /// A transform being lazy is distinct from a transformer being lazy. A 53 /// A transform being lazy is distinct from a transformer being lazy. A
53 /// transformer that's declaring but not lazy will have lazy transforms for 54 /// transformer that's declaring but not lazy will have lazy transforms for
54 /// primary inputs that are themselves lazy. 55 /// primary inputs that are themselves lazy.
Bob Nystrom 2014/04/10 22:58:56 I think we should disambiguate these terms. How ab
nweiz 2014/04/11 00:47:59 Done.
55 bool _isLazy; 56 final bool _isLazy;
57
58 /// Whether this is a lazy transform waiting for [force] to be called to
59 /// generate inputs.
60 ///
61 /// This defaults to `true` for lazy transforms and `false` otherwise. During
62 /// or after running `isPrimary` or `declareOutputs`, this may become `false`,
63 /// indicating that the transform has been forced and should generate outputs
64 /// as soon as possible. It will only be set back to `true` if an input
65 /// changes *after* `apply` has completed.
66 bool _awaitingForce;
Bob Nystrom 2014/04/10 22:58:56 This name is a bit confusing. How about "_deferred
nweiz 2014/04/11 00:47:59 That doesn't match your previous suggestion. You s
Bob Nystrom 2014/04/14 19:20:25 Ah, good point.
56 67
57 /// The subscriptions to each input's [AssetNode.onStateChange] stream. 68 /// The subscriptions to each input's [AssetNode.onStateChange] stream.
58 final _inputSubscriptions = new Map<AssetId, StreamSubscription>(); 69 final _inputSubscriptions = new Map<AssetId, StreamSubscription>();
59 70
60 /// The controllers for the asset nodes emitted by this node. 71 /// The controllers for the asset nodes emitted by this node.
61 final _outputControllers = new Map<AssetId, AssetNodeController>(); 72 final _outputControllers = new Map<AssetId, AssetNodeController>();
62 73
63 /// The ids of inputs the transformer tried and failed to read last time it 74 /// The ids of inputs the transformer tried and failed to read last time it
64 /// ran. 75 /// ran.
65 final _missingInputs = new Set<AssetId>(); 76 final _missingInputs = new Set<AssetId>();
(...skipping 27 matching lines...) Expand all
93 /// This is synchronous because error logs can cause the transform to fail, so 104 /// This is synchronous because error logs can cause the transform to fail, so
94 /// we need to ensure that their processing isn't delayed until after the 105 /// we need to ensure that their processing isn't delayed until after the
95 /// transform or build has finished. 106 /// transform or build has finished.
96 Stream<LogEntry> get onLog => _onLogPool.stream; 107 Stream<LogEntry> get onLog => _onLogPool.stream;
97 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 108 final _onLogPool = new StreamPool<LogEntry>.broadcast();
98 109
99 /// A controller for log entries emitted by this node. 110 /// A controller for log entries emitted by this node.
100 final _onLogController = new StreamController<LogEntry>.broadcast(sync: true); 111 final _onLogController = new StreamController<LogEntry>.broadcast(sync: true);
101 112
102 /// The current state of [this]. 113 /// The current state of [this].
103 var _state = _State.COMPUTING_IS_PRIMARY; 114 var _state = _State.DECLARING;
104 115
105 /// Whether [this] has been marked as removed. 116 /// Whether [this] has been marked as removed.
106 bool get _isRemoved => _onAssetController.isClosed; 117 bool get _isRemoved => _onAssetController.isClosed;
107 118
108 /// Whether the most recent run of this transform has declared that it 119 /// Whether the most recent run of this transform has declared that it
109 /// consumes the primary input. 120 /// consumes the primary input.
110 /// 121 ///
111 /// Defaults to `false`. This is not meaningful unless [_state] is 122 /// Defaults to `false`. This is not meaningful unless [_state] is
112 /// [_State.APPLIED]. 123 /// [_State.APPLIED] or [_State.DECLARED].
113 bool _consumePrimary = false; 124 bool _consumePrimary = false;
114 125
115 /// The set of output ids that [transformer] declared it would emit. 126 /// The set of output ids that [transformer] declared it would emit.
116 /// 127 ///
117 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its 128 /// This is only non-null if [transformer] is a [DeclaringTransformer] and its
118 /// [declareOutputs] has been run successfully. 129 /// [declareOutputs] has been run successfully.
119 Set<AssetId> _declaredOutputs; 130 Set<AssetId> _declaredOutputs;
120 131
121 TransformNode(this.phase, Transformer transformer, AssetNode primary, 132 TransformNode(this.phase, Transformer transformer, AssetNode primary,
122 this._location) 133 this._location)
123 : transformer = transformer, 134 : transformer = transformer,
124 primary = primary, 135 primary = primary,
125 _isLazy = transformer is LazyTransformer || 136 _isLazy = transformer is LazyTransformer ||
126 (transformer is DeclaringTransformer && primary.isLazy) { 137 (transformer is DeclaringTransformer && primary.isLazy) {
138 _awaitingForce = _isLazy;
139
127 _onLogPool.add(_onLogController.stream); 140 _onLogPool.add(_onLogController.stream);
128 141
129 if (!_isLazy) primary.force(); 142 if (!_isLazy) primary.force();
130 143
131 _primarySubscription = primary.onStateChange.listen((state) { 144 _primarySubscription = primary.onStateChange.listen((state) {
132 if (state.isRemoved) { 145 if (state.isRemoved) {
133 remove(); 146 remove();
134 } else { 147 } else {
148 if (state.isDirty && !_isLazy) primary.force();
135 _dirty(); 149 _dirty();
136 } 150 }
137 }); 151 });
138 152
139 _phaseSubscription = phase.previous.onAsset.listen((node) { 153 _phaseSubscription = phase.previous.onAsset.listen((node) {
140 if (_missingInputs.contains(node.id)) _dirty(); 154 if (!_missingInputs.contains(node.id)) return;
155 if (!_isLazy) node.force();
156 _dirty();
141 }); 157 });
142 158
143 _isPrimary(); 159 _isPrimary();
144 } 160 }
145 161
146 /// The [TransformInfo] describing this node. 162 /// The [TransformInfo] describing this node.
147 /// 163 ///
148 /// [TransformInfo] is the publicly-visible representation of a transform 164 /// [TransformInfo] is the publicly-visible representation of a transform
149 /// node. 165 /// node.
150 TransformInfo get info => new TransformInfo(transformer, primary.id); 166 TransformInfo get info => new TransformInfo(transformer, primary.id);
(...skipping 14 matching lines...) Expand all
165 _clearOutputs(); 181 _clearOutputs();
166 if (_passThroughController != null) { 182 if (_passThroughController != null) {
167 _passThroughController.setRemoved(); 183 _passThroughController.setRemoved();
168 _passThroughController = null; 184 _passThroughController = null;
169 } 185 }
170 } 186 }
171 187
172 /// If [transformer] is lazy, ensures that its concrete outputs will be 188 /// If [transformer] is lazy, ensures that its concrete outputs will be
173 /// generated. 189 /// generated.
174 void force() { 190 void force() {
175 // TODO(nweiz): we might want to have a timeout after which, if the 191 if (!_awaitingForce) return;
176 // transform's outputs have gone unused, we switch it back to lazy mode.
177 if (!_isLazy) return;
178 primary.force(); 192 primary.force();
179 _isLazy = false; 193 _awaitingForce = false;
180 _dirty(); 194 _dirty();
181 } 195 }
182 196
183 /// Marks this transform as dirty. 197 /// Marks this transform as dirty.
184 /// 198 ///
185 /// This causes all of the transform's outputs to be marked as dirty as well. 199 /// This causes all of the transform's outputs to be marked as dirty as well.
186 void _dirty() { 200 void _dirty() {
187 if (_state == _State.NOT_PRIMARY) { 201 if (_state == _State.NOT_PRIMARY) {
188 _emitPassThrough(); 202 _emitPassThrough();
189 return; 203 return;
190 } 204 }
191 if (_state == _State.COMPUTING_IS_PRIMARY || _isLazy) return; 205 if (_state == _State.DECLARING || _awaitingForce) return;
Bob Nystrom 2014/04/10 22:58:56 Add some documentation here about what the DECLARI
nweiz 2014/04/11 00:47:59 Done.
206
207 if (_state == _State.APPLIED && _isLazy) {
208 for (var controller in _outputControllers.values) {
209 controller.setLazy(force);
210 }
211 _state = _State.DECLARED;
Bob Nystrom 2014/04/10 22:58:56 This could use some docs here. It isn't obvious wh
nweiz 2014/04/11 00:47:59 Done.
212 _awaitingForce = true;
213 return;
214 }
192 215
193 if (_passThroughController != null) _passThroughController.setDirty(); 216 if (_passThroughController != null) _passThroughController.setDirty();
194 for (var controller in _outputControllers.values) { 217 for (var controller in _outputControllers.values) {
195 controller.setDirty(); 218 controller.setDirty();
196 } 219 }
197 220
198 if (_state == _State.APPLIED) { 221 if (_state == _State.APPLIED || _state == _State.DECLARED) {
199 _apply(); 222 _apply();
200 } else { 223 } else {
201 _state = _State.NEEDS_APPLY; 224 _state = _State.NEEDS_APPLY;
202 } 225 }
203 } 226 }
204 227
205 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the 228 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the
206 /// result. 229 /// result.
207 /// 230 ///
208 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. 231 /// This will also run [_declareOutputs] and/or [_apply] as appropriate.
209 void _isPrimary() { 232 void _isPrimary() {
210 syncFuture(() => transformer.isPrimary(primary.id)) 233 syncFuture(() => transformer.isPrimary(primary.id))
211 .catchError((error, stackTrace) { 234 .catchError((error, stackTrace) {
212 if (_isRemoved) return false; 235 if (_isRemoved) return false;
213 236
214 // Catch all transformer errors and pipe them to the results stream. This 237 // Catch all transformer errors and pipe them to the results stream. This
215 // is so a broken transformer doesn't take down the whole graph. 238 // is so a broken transformer doesn't take down the whole graph.
216 phase.cascade.reportError(_wrapException(error, stackTrace)); 239 phase.cascade.reportError(_wrapException(error, stackTrace));
217 240
218 return false; 241 return false;
219 }).then((isPrimary) { 242 }).then((isPrimary) {
220 if (_isRemoved) return null; 243 if (_isRemoved) return null;
221 if (isPrimary) { 244 if (isPrimary) {
222 return _declareOutputs().then((_) { 245 return _declareOutputs().then((_) {
223 if (_isRemoved) return; 246 if (_isRemoved) return;
224 if (_isLazy) { 247 if (_awaitingForce) {
225 _state = _State.APPLIED; 248 _state = _State.DECLARED;
226 _onDoneController.add(null); 249 _onDoneController.add(null);
227 } else { 250 } else {
228 _apply(); 251 _apply();
229 } 252 }
230 }); 253 });
231 } 254 }
232 255
233 _emitPassThrough(); 256 _emitPassThrough();
234 _state = _State.NOT_PRIMARY; 257 _state = _State.NOT_PRIMARY;
235 _onDoneController.add(null); 258 _onDoneController.add(null);
(...skipping 19 matching lines...) Expand all
255 .where((id) => id.package != phase.cascade.package).toSet(); 278 .where((id) => id.package != phase.cascade.package).toSet();
256 for (var id in invalidIds) { 279 for (var id in invalidIds) {
257 _declaredOutputs.remove(id); 280 _declaredOutputs.remove(id);
258 // TODO(nweiz): report this as a warning rather than a failing error. 281 // TODO(nweiz): report this as a warning rather than a failing error.
259 phase.cascade.reportError(new InvalidOutputException(info, id)); 282 phase.cascade.reportError(new InvalidOutputException(info, id));
260 } 283 }
261 284
262 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); 285 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough();
263 286
264 for (var id in _declaredOutputs) { 287 for (var id in _declaredOutputs) {
265 var controller = _isLazy 288 var controller = _awaitingForce
266 ? new AssetNodeController.lazy(id, force, this) 289 ? new AssetNodeController.lazy(id, force, this)
267 : new AssetNodeController(id, this); 290 : new AssetNodeController(id, this);
268 _outputControllers[id] = controller; 291 _outputControllers[id] = controller;
269 _onAssetController.add(controller.node); 292 _onAssetController.add(controller.node);
270 } 293 }
271 }).catchError((error, stackTrace) { 294 }).catchError((error, stackTrace) {
272 if (_isRemoved) return; 295 if (_isRemoved) return;
273 phase.cascade.reportError(_wrapException(error, stackTrace)); 296 phase.cascade.reportError(_wrapException(error, stackTrace));
274 }); 297 });
275 } 298 }
276 299
277 /// Applies this transform. 300 /// Applies this transform.
278 void _apply() { 301 void _apply() {
279 assert(!_isRemoved && !_isLazy); 302 assert(!_isRemoved && !_awaitingForce);
280 303
281 // Clear input subscriptions here as well as in [_process] because [_apply] 304 // Clear input subscriptions here as well as in [_process] because [_apply]
282 // may be restarted independently if only a secondary input changes. 305 // may be restarted independently if only a secondary input changes.
283 _clearInputSubscriptions(); 306 _clearInputSubscriptions();
284 _state = _State.APPLYING; 307 _state = _State.APPLYING;
285 _runApply().then((hadError) { 308 _runApply().then((hadError) {
286 if (_isRemoved) return; 309 if (_isRemoved) return;
287 310
288 if (_state == _State.NEEDS_APPLY) { 311 if (_state == _State.NEEDS_APPLY) {
289 _apply(); 312 _apply();
(...skipping 27 matching lines...) Expand all
317 return phase.previous.getOutput(id).then((node) { 340 return phase.previous.getOutput(id).then((node) {
318 // Throw if the input isn't found. This ensures the transformer's apply 341 // Throw if the input isn't found. This ensures the transformer's apply
319 // is exited. We'll then catch this and report it through the proper 342 // is exited. We'll then catch this and report it through the proper
320 // results stream. 343 // results stream.
321 if (node == null) { 344 if (node == null) {
322 _missingInputs.add(id); 345 _missingInputs.add(id);
323 throw new AssetNotFoundException(id); 346 throw new AssetNotFoundException(id);
324 } 347 }
325 348
326 _inputSubscriptions.putIfAbsent(node.id, () { 349 _inputSubscriptions.putIfAbsent(node.id, () {
327 return node.onStateChange.listen((_) => _dirty()); 350 return node.onStateChange.listen((state) {
351 if (state.isDirty) node.force();
Bob Nystrom 2014/04/10 22:58:56 So if an input to this transform changes to a dirt
nweiz 2014/04/11 00:47:59 Removed, as per discussion.
352 _dirty();
353 });
328 }); 354 });
329 355
330 return node.asset; 356 return node.asset;
331 }); 357 });
332 } 358 }
333 359
334 /// Run [Transformer.apply] as soon as [primary] is available. 360 /// Run [Transformer.apply] as soon as [primary] is available.
335 /// 361 ///
336 /// Returns whether or not an error occurred while running the transformer. 362 /// Returns whether or not an error occurred while running the transformer.
337 Future<bool> _runApply() { 363 Future<bool> _runApply() {
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 _onLogController.add( 494 _onLogController.add(
469 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); 495 new LogEntry(info, primary.id, LogLevel.WARNING, message, null));
470 } 496 }
471 497
472 String toString() => 498 String toString() =>
473 "transform node in $_location for $transformer on $primary"; 499 "transform node in $_location for $transformer on $primary";
474 } 500 }
475 501
476 /// The enum of states that [TransformNode] can be in. 502 /// The enum of states that [TransformNode] can be in.
477 class _State { 503 class _State {
478 /// The transform is running [Transformer.isPrimary]. 504 /// The transform is running [Transformer.isPrimary] followed by
505 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]).
479 /// 506 ///
480 /// This is the initial state of the transformer. Once [Transformer.isPrimary] 507 /// This is the initial state of the transformer, and it will only occur once
481 /// finishes running, this will transition to [APPLYING] if the input is 508 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs]
482 /// primary, or [NOT_PRIMARY] if it's not. 509 /// are independent of the contents of the primary input. Once the two methods
483 static final COMPUTING_IS_PRIMARY = const _State._("computing isPrimary"); 510 /// finish running, this will transition to [NOT_PRIMARY] if the input isn't
511 /// primary, [DECLARED] if the transform is lazy, and [APPLYING] otherwise.
512 static final DECLARING = const _State._("computing isPrimary");
513
514 /// The transform is lazy and has run [DeclaringTransformer.declareOutputs]
515 /// but hasn't yet been forced.
516 ///
517 /// This will transition to [APPLYING] when one of the outputs has been
518 /// forced.
519 static final DECLARED = const _State._("declared");
Bob Nystrom 2014/04/10 22:58:56 "DEFERRED"?
nweiz 2014/04/11 00:47:59 That would be confusing in light of your terminolo
484 520
485 /// The transform is running [Transformer.apply]. 521 /// The transform is running [Transformer.apply].
486 /// 522 ///
487 /// If an input changes while in this state, it will transition to 523 /// If an input changes while in this state, it will transition to
488 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when 524 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when
489 /// [Transformer.apply] finishes running, it will transition to [APPLIED]. 525 /// [Transformer.apply] finishes running, it will transition to [APPLIED].
490 static final APPLYING = const _State._("applying"); 526 static final APPLYING = const _State._("applying");
491 527
492 /// The transform is running [Transformer.apply], but an input changed after 528 /// The transform is running [Transformer.apply], but an input changed after
493 /// it started, so it will need to re-run [Transformer.apply]. 529 /// it started, so it will need to re-run [Transformer.apply].
494 /// 530 ///
495 /// This will transition to [APPLYING] once [Transformer.apply] finishes 531 /// This will transition to [APPLYING] once [Transformer.apply] finishes
496 /// running. 532 /// running.
497 static final NEEDS_APPLY = const _State._("needs apply"); 533 static final NEEDS_APPLY = const _State._("needs apply");
498 534
499 /// The transform has finished running [Transformer.apply], whether or not it 535 /// The transform has finished running [Transformer.apply], whether or not it
500 /// emitted an error. 536 /// emitted an error.
501 /// 537 ///
502 /// If the transformer is lazy, the [TransformNode] can also be in this state 538 /// If the transformer is lazy, the [TransformNode] can also be in this state
503 /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has 539 /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has
504 /// not. 540 /// not.
505 /// 541 ///
506 /// If an input changes, this will transition to [APPLYING]. 542 /// If an input changes, this will transition to [DECLARED] if the transform
543 /// is lazy and [APPLYING] otherwise.
507 static final APPLIED = const _State._("applied"); 544 static final APPLIED = const _State._("applied");
508 545
509 /// The transform has finished running [Transformer.isPrimary], which returned 546 /// The transform has finished running [Transformer.isPrimary], which returned
510 /// `false`. 547 /// `false`.
511 /// 548 ///
512 /// This will never transition to another state. 549 /// This will never transition to another state.
513 static final NOT_PRIMARY = const _State._("not primary"); 550 static final NOT_PRIMARY = const _State._("not primary");
514 551
515 final String name; 552 final String name;
516 553
517 const _State._(this.name); 554 const _State._(this.name);
518 555
519 String toString() => name; 556 String toString() => name;
520 } 557 }
OLDNEW
« no previous file with comments | « no previous file | pkg/barback/test/package_graph/lazy_transformer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698