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

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: code review + disabling test 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
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 deferred.
51 /// 52 ///
52 /// A transform being lazy is distinct from a transformer being lazy. A 53 /// A transform is deferred if either its transformer is lazy or if its
53 /// transformer that's declaring but not lazy will have lazy transforms for 54 /// transformer is declaring and its primary input comes from a deferred
54 /// primary inputs that are themselves lazy. 55 /// transformer.
55 bool _isLazy; 56 final bool deferred;
57
58 /// Whether this is a deferred transform waiting for [force] to be called to
59 /// generate inputs.
60 ///
61 /// This defaults to `true` for deferred transforms and `false` otherwise.
62 /// During or after running `isPrimary` or `declareOutputs`, this may become
63 /// `false`, indicating that the transform has been forced and should generate
64 /// outputs as soon as possible. It will only be set back to `true` if an
65 /// input changes *after* `apply` has completed.
66 bool _awaitingForce;
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 deferred = transformer is LazyTransformer ||
126 (transformer is DeclaringTransformer && primary.isLazy) { 137 (transformer is DeclaringTransformer && primary.deferred) {
138 _awaitingForce = deferred;
139
127 _onLogPool.add(_onLogController.stream); 140 _onLogPool.add(_onLogController.stream);
128 141
129 if (!_isLazy) primary.force(); 142 if (!deferred) 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 && !deferred) 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 (!deferred) 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 11 matching lines...) Expand all
162 _primarySubscription.cancel(); 178 _primarySubscription.cancel();
163 _phaseSubscription.cancel(); 179 _phaseSubscription.cancel();
164 _clearInputSubscriptions(); 180 _clearInputSubscriptions();
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 [this] is deferred, 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
206 // If we're in the process of running [isPrimary] or [declareOutputs], we
207 // already know that [apply] needs to be run so there's nothing we need to
208 // mark as dirty.
209 if (_state == _State.DECLARING) return;
210
211 // If we're waiting until [force] is called to run [apply], we don't want to
212 // run [apply] too early.
213 if (_awaitingForce) return;
214
215 if (_state == _State.APPLIED && deferred) {
216 // Transition to DECLARED, indicating that we know what outputs [apply]
217 // will emit but we're waiting to emit them concretely until [force] is
218 // called.
219 _state = _State.DECLARED;
220 _awaitingForce = true;
221 for (var controller in _outputControllers.values) {
222 controller.setLazy(force);
223 }
224 return;
225 }
192 226
193 if (_passThroughController != null) _passThroughController.setDirty(); 227 if (_passThroughController != null) _passThroughController.setDirty();
194 for (var controller in _outputControllers.values) { 228 for (var controller in _outputControllers.values) {
195 controller.setDirty(); 229 controller.setDirty();
196 } 230 }
197 231
198 if (_state == _State.APPLIED) { 232 if (_state == _State.APPLIED || _state == _State.DECLARED) {
199 _apply(); 233 _apply();
200 } else { 234 } else {
201 _state = _State.NEEDS_APPLY; 235 _state = _State.NEEDS_APPLY;
202 } 236 }
203 } 237 }
204 238
205 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the 239 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the
206 /// result. 240 /// result.
207 /// 241 ///
208 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. 242 /// This will also run [_declareOutputs] and/or [_apply] as appropriate.
209 void _isPrimary() { 243 void _isPrimary() {
210 syncFuture(() => transformer.isPrimary(primary.id)) 244 syncFuture(() => transformer.isPrimary(primary.id))
211 .catchError((error, stackTrace) { 245 .catchError((error, stackTrace) {
212 if (_isRemoved) return false; 246 if (_isRemoved) return false;
213 247
214 // Catch all transformer errors and pipe them to the results stream. This 248 // 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. 249 // is so a broken transformer doesn't take down the whole graph.
216 phase.cascade.reportError(_wrapException(error, stackTrace)); 250 phase.cascade.reportError(_wrapException(error, stackTrace));
217 251
218 return false; 252 return false;
219 }).then((isPrimary) { 253 }).then((isPrimary) {
220 if (_isRemoved) return null; 254 if (_isRemoved) return null;
221 if (isPrimary) { 255 if (isPrimary) {
222 return _declareOutputs().then((_) { 256 return _declareOutputs().then((_) {
223 if (_isRemoved) return; 257 if (_isRemoved) return;
224 if (_isLazy) { 258 if (_awaitingForce) {
225 _state = _State.APPLIED; 259 _state = _State.DECLARED;
226 _onDoneController.add(null); 260 _onDoneController.add(null);
227 } else { 261 } else {
228 _apply(); 262 _apply();
229 } 263 }
230 }); 264 });
231 } 265 }
232 266
233 _emitPassThrough(); 267 _emitPassThrough();
234 _state = _State.NOT_PRIMARY; 268 _state = _State.NOT_PRIMARY;
235 _onDoneController.add(null); 269 _onDoneController.add(null);
(...skipping 19 matching lines...) Expand all
255 .where((id) => id.package != phase.cascade.package).toSet(); 289 .where((id) => id.package != phase.cascade.package).toSet();
256 for (var id in invalidIds) { 290 for (var id in invalidIds) {
257 _declaredOutputs.remove(id); 291 _declaredOutputs.remove(id);
258 // TODO(nweiz): report this as a warning rather than a failing error. 292 // TODO(nweiz): report this as a warning rather than a failing error.
259 phase.cascade.reportError(new InvalidOutputException(info, id)); 293 phase.cascade.reportError(new InvalidOutputException(info, id));
260 } 294 }
261 295
262 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); 296 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough();
263 297
264 for (var id in _declaredOutputs) { 298 for (var id in _declaredOutputs) {
265 var controller = _isLazy 299 var controller = _awaitingForce
266 ? new AssetNodeController.lazy(id, force, this) 300 ? new AssetNodeController.lazy(id, force, this)
267 : new AssetNodeController(id, this); 301 : new AssetNodeController(id, this);
268 _outputControllers[id] = controller; 302 _outputControllers[id] = controller;
269 _onAssetController.add(controller.node); 303 _onAssetController.add(controller.node);
270 } 304 }
271 }).catchError((error, stackTrace) { 305 }).catchError((error, stackTrace) {
272 if (_isRemoved) return; 306 if (_isRemoved) return;
273 phase.cascade.reportError(_wrapException(error, stackTrace)); 307 phase.cascade.reportError(_wrapException(error, stackTrace));
274 }); 308 });
275 } 309 }
276 310
277 /// Applies this transform. 311 /// Applies this transform.
278 void _apply() { 312 void _apply() {
279 assert(!_isRemoved && !_isLazy); 313 assert(!_isRemoved && !_awaitingForce);
280 314
281 // Clear input subscriptions here as well as in [_process] because [_apply] 315 // Clear input subscriptions here as well as in [_process] because [_apply]
282 // may be restarted independently if only a secondary input changes. 316 // may be restarted independently if only a secondary input changes.
283 _clearInputSubscriptions(); 317 _clearInputSubscriptions();
284 _state = _State.APPLYING; 318 _state = _State.APPLYING;
285 _runApply().then((hadError) { 319 _runApply().then((hadError) {
286 if (_isRemoved) return; 320 if (_isRemoved) return;
287 321
288 if (_state == _State.NEEDS_APPLY) { 322 if (_state == _State.NEEDS_APPLY) {
289 _apply(); 323 _apply();
(...skipping 27 matching lines...) Expand all
317 return phase.previous.getOutput(id).then((node) { 351 return phase.previous.getOutput(id).then((node) {
318 // Throw if the input isn't found. This ensures the transformer's apply 352 // 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 353 // is exited. We'll then catch this and report it through the proper
320 // results stream. 354 // results stream.
321 if (node == null) { 355 if (node == null) {
322 _missingInputs.add(id); 356 _missingInputs.add(id);
323 throw new AssetNotFoundException(id); 357 throw new AssetNotFoundException(id);
324 } 358 }
325 359
326 _inputSubscriptions.putIfAbsent(node.id, () { 360 _inputSubscriptions.putIfAbsent(node.id, () {
327 return node.onStateChange.listen((_) => _dirty()); 361 return node.onStateChange.listen((state) => _dirty());
328 }); 362 });
329 363
330 return node.asset; 364 return node.asset;
331 }); 365 });
332 } 366 }
333 367
334 /// Run [Transformer.apply] as soon as [primary] is available. 368 /// Run [Transformer.apply] as soon as [primary] is available.
335 /// 369 ///
336 /// Returns whether or not an error occurred while running the transformer. 370 /// Returns whether or not an error occurred while running the transformer.
337 Future<bool> _runApply() { 371 Future<bool> _runApply() {
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 _onLogController.add( 502 _onLogController.add(
469 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); 503 new LogEntry(info, primary.id, LogLevel.WARNING, message, null));
470 } 504 }
471 505
472 String toString() => 506 String toString() =>
473 "transform node in $_location for $transformer on $primary"; 507 "transform node in $_location for $transformer on $primary";
474 } 508 }
475 509
476 /// The enum of states that [TransformNode] can be in. 510 /// The enum of states that [TransformNode] can be in.
477 class _State { 511 class _State {
478 /// The transform is running [Transformer.isPrimary]. 512 /// The transform is running [Transformer.isPrimary] followed by
513 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]).
479 /// 514 ///
480 /// This is the initial state of the transformer. Once [Transformer.isPrimary] 515 /// 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 516 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs]
482 /// primary, or [NOT_PRIMARY] if it's not. 517 /// are independent of the contents of the primary input. Once the two methods
483 static final COMPUTING_IS_PRIMARY = const _State._("computing isPrimary"); 518 /// finish running, this will transition to [NOT_PRIMARY] if the input isn't
519 /// primary, [DECLARED] if the transform is deferred, and [APPLYING]
520 /// otherwise.
521 static final DECLARING = const _State._("computing isPrimary");
522
523 /// The transform is deferred and has run
524 /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced.
525 ///
526 /// This will transition to [APPLYING] when one of the outputs has been
527 /// forced.
528 static final DECLARED = const _State._("declared");
484 529
485 /// The transform is running [Transformer.apply]. 530 /// The transform is running [Transformer.apply].
486 /// 531 ///
487 /// If an input changes while in this state, it will transition to 532 /// If an input changes while in this state, it will transition to
488 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when 533 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when
489 /// [Transformer.apply] finishes running, it will transition to [APPLIED]. 534 /// [Transformer.apply] finishes running, it will transition to [APPLIED].
490 static final APPLYING = const _State._("applying"); 535 static final APPLYING = const _State._("applying");
491 536
492 /// The transform is running [Transformer.apply], but an input changed after 537 /// The transform is running [Transformer.apply], but an input changed after
493 /// it started, so it will need to re-run [Transformer.apply]. 538 /// it started, so it will need to re-run [Transformer.apply].
494 /// 539 ///
495 /// This will transition to [APPLYING] once [Transformer.apply] finishes 540 /// This will transition to [APPLYING] once [Transformer.apply] finishes
496 /// running. 541 /// running.
497 static final NEEDS_APPLY = const _State._("needs apply"); 542 static final NEEDS_APPLY = const _State._("needs apply");
498 543
499 /// The transform has finished running [Transformer.apply], whether or not it 544 /// The transform has finished running [Transformer.apply], whether or not it
500 /// emitted an error. 545 /// emitted an error.
501 /// 546 ///
502 /// If the transformer is lazy, the [TransformNode] can also be in this state 547 /// If the transformer is deferred, the [TransformNode] can also be in this
503 /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has 548 /// state when [Transformer.declareOutputs] has been run but
504 /// not. 549 /// [Transformer.apply] has not.
505 /// 550 ///
506 /// If an input changes, this will transition to [APPLYING]. 551 /// If an input changes, this will transition to [DECLARED] if the transform
552 /// is deferred and [APPLYING] otherwise.
507 static final APPLIED = const _State._("applied"); 553 static final APPLIED = const _State._("applied");
508 554
509 /// The transform has finished running [Transformer.isPrimary], which returned 555 /// The transform has finished running [Transformer.isPrimary], which returned
510 /// `false`. 556 /// `false`.
511 /// 557 ///
512 /// This will never transition to another state. 558 /// This will never transition to another state.
513 static final NOT_PRIMARY = const _State._("not primary"); 559 static final NOT_PRIMARY = const _State._("not primary");
514 560
515 final String name; 561 final String name;
516 562
517 const _State._(this.name); 563 const _State._(this.name);
518 564
519 String toString() => name; 565 String toString() => name;
520 } 566 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_node.dart ('k') | pkg/barback/test/package_graph/declaring_transformer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698