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

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 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 &&
138 primary.transform != null &&
139 primary.transform.deferred) {
Bob Nystrom 2014/04/14 19:20:25 Instead of checking for a null transform here, wha
nweiz 2014/04/14 21:55:07 Done.
140 _awaitingForce = deferred;
141
127 _onLogPool.add(_onLogController.stream); 142 _onLogPool.add(_onLogController.stream);
128 143
129 if (!_isLazy) primary.force(); 144 if (!deferred) primary.force();
130 145
131 _primarySubscription = primary.onStateChange.listen((state) { 146 _primarySubscription = primary.onStateChange.listen((state) {
132 if (state.isRemoved) { 147 if (state.isRemoved) {
133 remove(); 148 remove();
134 } else { 149 } else {
150 if (state.isDirty && !deferred) primary.force();
135 _dirty(); 151 _dirty();
136 } 152 }
137 }); 153 });
138 154
139 _phaseSubscription = phase.previous.onAsset.listen((node) { 155 _phaseSubscription = phase.previous.onAsset.listen((node) {
140 if (_missingInputs.contains(node.id)) _dirty(); 156 if (!_missingInputs.contains(node.id)) return;
157 if (!deferred) node.force();
158 _dirty();
141 }); 159 });
142 160
143 _isPrimary(); 161 _isPrimary();
144 } 162 }
145 163
146 /// The [TransformInfo] describing this node. 164 /// The [TransformInfo] describing this node.
147 /// 165 ///
148 /// [TransformInfo] is the publicly-visible representation of a transform 166 /// [TransformInfo] is the publicly-visible representation of a transform
149 /// node. 167 /// node.
150 TransformInfo get info => new TransformInfo(transformer, primary.id); 168 TransformInfo get info => new TransformInfo(transformer, primary.id);
(...skipping 11 matching lines...) Expand all
162 _primarySubscription.cancel(); 180 _primarySubscription.cancel();
163 _phaseSubscription.cancel(); 181 _phaseSubscription.cancel();
164 _clearInputSubscriptions(); 182 _clearInputSubscriptions();
165 _clearOutputs(); 183 _clearOutputs();
166 if (_passThroughController != null) { 184 if (_passThroughController != null) {
167 _passThroughController.setRemoved(); 185 _passThroughController.setRemoved();
168 _passThroughController = null; 186 _passThroughController = null;
169 } 187 }
170 } 188 }
171 189
172 /// If [transformer] is lazy, ensures that its concrete outputs will be 190 /// If [this] is deferred, ensures that its concrete outputs will be
173 /// generated. 191 /// generated.
174 void force() { 192 void force() {
175 // TODO(nweiz): we might want to have a timeout after which, if the 193 if (!_awaitingForce) return;
176 // transform's outputs have gone unused, we switch it back to lazy mode.
177 if (!_isLazy) return;
178 primary.force(); 194 primary.force();
179 _isLazy = false; 195 _awaitingForce = false;
180 _dirty(); 196 _dirty();
181 } 197 }
182 198
183 /// Marks this transform as dirty. 199 /// Marks this transform as dirty.
184 /// 200 ///
185 /// This causes all of the transform's outputs to be marked as dirty as well. 201 /// This causes all of the transform's outputs to be marked as dirty as well.
186 void _dirty() { 202 void _dirty() {
187 if (_state == _State.NOT_PRIMARY) { 203 if (_state == _State.NOT_PRIMARY) {
188 _emitPassThrough(); 204 _emitPassThrough();
189 return; 205 return;
190 } 206 }
191 if (_state == _State.COMPUTING_IS_PRIMARY || _isLazy) return; 207
208 // If we're in the process of running [isPrimary] or [declareOutputs], we're
Bob Nystrom 2014/04/14 19:20:25 "we're" -> "we"
nweiz 2014/04/14 21:55:07 Done.
209 // already know that [apply] needs to be run so there's nothing we need to
210 // mark as dirty.
211 if (_state == _State.DECLARING) return;
212
213 // If we're waiting until [force] is called to run [apply], we don't to run
Bob Nystrom 2014/04/14 19:20:25 "don't to" -> "don't want to".
nweiz 2014/04/14 21:55:07 Done.
214 // [apply] too early.
215 if (_awaitingForce) return;
216
217 if (_state == _State.APPLIED && deferred) {
218 // Transition to DECLARED, indicating that we know what outputs [apply]
219 // will emit but we're waiting to emit them concretely until [force] is
220 // called.
221 _state = _State.DECLARED;
222 _awaitingForce = true;
223 for (var controller in _outputControllers.values) {
224 controller.setLazy(force);
225 }
226 return;
227 }
192 228
193 if (_passThroughController != null) _passThroughController.setDirty(); 229 if (_passThroughController != null) _passThroughController.setDirty();
194 for (var controller in _outputControllers.values) { 230 for (var controller in _outputControllers.values) {
195 controller.setDirty(); 231 // Don't re-mark a controller as dirty to avoid cases where we try to
232 // dispatch an event while handling another event (e.g. an output is
233 // marked lazy, which causes it to be forced, which causes it to be marked
234 // dirty).
235 if (!controller.node.state.isDirty) controller.setDirty();
Bob Nystrom 2014/04/14 19:20:25 Can this check be moved into .setDirty()?
nweiz 2014/04/14 21:55:07 Done.
196 } 236 }
197 237
198 if (_state == _State.APPLIED) { 238 if (_state == _State.APPLIED || _state == _State.DECLARED) {
199 _apply(); 239 _apply();
200 } else { 240 } else {
201 _state = _State.NEEDS_APPLY; 241 _state = _State.NEEDS_APPLY;
202 } 242 }
203 } 243 }
204 244
205 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the 245 /// Runs [transformer.isPrimary] and adjusts [this]'s state according to the
206 /// result. 246 /// result.
207 /// 247 ///
208 /// This will also run [_declareOutputs] and/or [_apply] as appropriate. 248 /// This will also run [_declareOutputs] and/or [_apply] as appropriate.
209 void _isPrimary() { 249 void _isPrimary() {
210 syncFuture(() => transformer.isPrimary(primary.id)) 250 syncFuture(() => transformer.isPrimary(primary.id))
211 .catchError((error, stackTrace) { 251 .catchError((error, stackTrace) {
212 if (_isRemoved) return false; 252 if (_isRemoved) return false;
213 253
214 // Catch all transformer errors and pipe them to the results stream. This 254 // 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. 255 // is so a broken transformer doesn't take down the whole graph.
216 phase.cascade.reportError(_wrapException(error, stackTrace)); 256 phase.cascade.reportError(_wrapException(error, stackTrace));
217 257
218 return false; 258 return false;
219 }).then((isPrimary) { 259 }).then((isPrimary) {
220 if (_isRemoved) return null; 260 if (_isRemoved) return null;
221 if (isPrimary) { 261 if (isPrimary) {
222 return _declareOutputs().then((_) { 262 return _declareOutputs().then((_) {
223 if (_isRemoved) return; 263 if (_isRemoved) return;
224 if (_isLazy) { 264 if (_awaitingForce) {
225 _state = _State.APPLIED; 265 _state = _State.DECLARED;
226 _onDoneController.add(null); 266 _onDoneController.add(null);
227 } else { 267 } else {
228 _apply(); 268 _apply();
229 } 269 }
230 }); 270 });
231 } 271 }
232 272
233 _emitPassThrough(); 273 _emitPassThrough();
234 _state = _State.NOT_PRIMARY; 274 _state = _State.NOT_PRIMARY;
235 _onDoneController.add(null); 275 _onDoneController.add(null);
(...skipping 19 matching lines...) Expand all
255 .where((id) => id.package != phase.cascade.package).toSet(); 295 .where((id) => id.package != phase.cascade.package).toSet();
256 for (var id in invalidIds) { 296 for (var id in invalidIds) {
257 _declaredOutputs.remove(id); 297 _declaredOutputs.remove(id);
258 // TODO(nweiz): report this as a warning rather than a failing error. 298 // TODO(nweiz): report this as a warning rather than a failing error.
259 phase.cascade.reportError(new InvalidOutputException(info, id)); 299 phase.cascade.reportError(new InvalidOutputException(info, id));
260 } 300 }
261 301
262 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough(); 302 if (!_declaredOutputs.contains(primary.id)) _emitPassThrough();
263 303
264 for (var id in _declaredOutputs) { 304 for (var id in _declaredOutputs) {
265 var controller = _isLazy 305 var controller = _awaitingForce
266 ? new AssetNodeController.lazy(id, force, this) 306 ? new AssetNodeController.lazy(id, force, this)
267 : new AssetNodeController(id, this); 307 : new AssetNodeController(id, this);
268 _outputControllers[id] = controller; 308 _outputControllers[id] = controller;
269 _onAssetController.add(controller.node); 309 _onAssetController.add(controller.node);
270 } 310 }
271 }).catchError((error, stackTrace) { 311 }).catchError((error, stackTrace) {
272 if (_isRemoved) return; 312 if (_isRemoved) return;
273 phase.cascade.reportError(_wrapException(error, stackTrace)); 313 phase.cascade.reportError(_wrapException(error, stackTrace));
274 }); 314 });
275 } 315 }
276 316
277 /// Applies this transform. 317 /// Applies this transform.
278 void _apply() { 318 void _apply() {
279 assert(!_isRemoved && !_isLazy); 319 assert(!_isRemoved && !_awaitingForce);
280 320
281 // Clear input subscriptions here as well as in [_process] because [_apply] 321 // Clear input subscriptions here as well as in [_process] because [_apply]
282 // may be restarted independently if only a secondary input changes. 322 // may be restarted independently if only a secondary input changes.
283 _clearInputSubscriptions(); 323 _clearInputSubscriptions();
284 _state = _State.APPLYING; 324 _state = _State.APPLYING;
285 _runApply().then((hadError) { 325 _runApply().then((hadError) {
286 if (_isRemoved) return; 326 if (_isRemoved) return;
287 327
288 if (_state == _State.NEEDS_APPLY) { 328 if (_state == _State.NEEDS_APPLY) {
289 _apply(); 329 _apply();
(...skipping 27 matching lines...) Expand all
317 return phase.previous.getOutput(id).then((node) { 357 return phase.previous.getOutput(id).then((node) {
318 // Throw if the input isn't found. This ensures the transformer's apply 358 // 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 359 // is exited. We'll then catch this and report it through the proper
320 // results stream. 360 // results stream.
321 if (node == null) { 361 if (node == null) {
322 _missingInputs.add(id); 362 _missingInputs.add(id);
323 throw new AssetNotFoundException(id); 363 throw new AssetNotFoundException(id);
324 } 364 }
325 365
326 _inputSubscriptions.putIfAbsent(node.id, () { 366 _inputSubscriptions.putIfAbsent(node.id, () {
327 return node.onStateChange.listen((_) => _dirty()); 367 return node.onStateChange.listen((state) => _dirty());
328 }); 368 });
329 369
330 return node.asset; 370 return node.asset;
331 }); 371 });
332 } 372 }
333 373
334 /// Run [Transformer.apply] as soon as [primary] is available. 374 /// Run [Transformer.apply] as soon as [primary] is available.
335 /// 375 ///
336 /// Returns whether or not an error occurred while running the transformer. 376 /// Returns whether or not an error occurred while running the transformer.
337 Future<bool> _runApply() { 377 Future<bool> _runApply() {
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 _onLogController.add( 508 _onLogController.add(
469 new LogEntry(info, primary.id, LogLevel.WARNING, message, null)); 509 new LogEntry(info, primary.id, LogLevel.WARNING, message, null));
470 } 510 }
471 511
472 String toString() => 512 String toString() =>
473 "transform node in $_location for $transformer on $primary"; 513 "transform node in $_location for $transformer on $primary";
474 } 514 }
475 515
476 /// The enum of states that [TransformNode] can be in. 516 /// The enum of states that [TransformNode] can be in.
477 class _State { 517 class _State {
478 /// The transform is running [Transformer.isPrimary]. 518 /// The transform is running [Transformer.isPrimary] followed by
519 /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]).
479 /// 520 ///
480 /// This is the initial state of the transformer. Once [Transformer.isPrimary] 521 /// 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 522 /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs]
482 /// primary, or [NOT_PRIMARY] if it's not. 523 /// are independent of the contents of the primary input. Once the two methods
483 static final COMPUTING_IS_PRIMARY = const _State._("computing isPrimary"); 524 /// finish running, this will transition to [NOT_PRIMARY] if the input isn't
525 /// primary, [DECLARED] if the transform is deferred, and [APPLYING] otherwise .
Bob Nystrom 2014/04/14 19:20:25 Long line.
nweiz 2014/04/14 21:55:07 Done.
526 static final DECLARING = const _State._("computing isPrimary");
527
528 /// The transform is deferred and has run [DeclaringTransformer.declareOutputs ]
529 /// but hasn't yet been forced.
530 ///
531 /// This will transition to [APPLYING] when one of the outputs has been
532 /// forced.
533 static final DECLARED = const _State._("declared");
484 534
485 /// The transform is running [Transformer.apply]. 535 /// The transform is running [Transformer.apply].
486 /// 536 ///
487 /// If an input changes while in this state, it will transition to 537 /// If an input changes while in this state, it will transition to
488 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when 538 /// [NEEDS_APPLY]. If the [TransformNode] is still in this state when
489 /// [Transformer.apply] finishes running, it will transition to [APPLIED]. 539 /// [Transformer.apply] finishes running, it will transition to [APPLIED].
490 static final APPLYING = const _State._("applying"); 540 static final APPLYING = const _State._("applying");
491 541
492 /// The transform is running [Transformer.apply], but an input changed after 542 /// The transform is running [Transformer.apply], but an input changed after
493 /// it started, so it will need to re-run [Transformer.apply]. 543 /// it started, so it will need to re-run [Transformer.apply].
494 /// 544 ///
495 /// This will transition to [APPLYING] once [Transformer.apply] finishes 545 /// This will transition to [APPLYING] once [Transformer.apply] finishes
496 /// running. 546 /// running.
497 static final NEEDS_APPLY = const _State._("needs apply"); 547 static final NEEDS_APPLY = const _State._("needs apply");
498 548
499 /// The transform has finished running [Transformer.apply], whether or not it 549 /// The transform has finished running [Transformer.apply], whether or not it
500 /// emitted an error. 550 /// emitted an error.
501 /// 551 ///
502 /// If the transformer is lazy, the [TransformNode] can also be in this state 552 /// If the transformer is deferred, the [TransformNode] can also be in this
503 /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has 553 /// state when [Transformer.declareOutputs] has been run but
504 /// not. 554 /// [Transformer.apply] has not.
505 /// 555 ///
506 /// If an input changes, this will transition to [APPLYING]. 556 /// If an input changes, this will transition to [DECLARED] if the transform
557 /// is deferred and [APPLYING] otherwise.
507 static final APPLIED = const _State._("applied"); 558 static final APPLIED = const _State._("applied");
508 559
509 /// The transform has finished running [Transformer.isPrimary], which returned 560 /// The transform has finished running [Transformer.isPrimary], which returned
510 /// `false`. 561 /// `false`.
511 /// 562 ///
512 /// This will never transition to another state. 563 /// This will never transition to another state.
513 static final NOT_PRIMARY = const _State._("not primary"); 564 static final NOT_PRIMARY = const _State._("not primary");
514 565
515 final String name; 566 final String name;
516 567
517 const _State._(this.name); 568 const _State._(this.name);
518 569
519 String toString() => name; 570 String toString() => name;
520 } 571 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_node.dart ('k') | pkg/barback/test/package_graph/lazy_transformer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698