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

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

Issue 200473006: Make barback's onAsset streams broadcast rather than single-subscriber. (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') | pkg/barback/lib/src/phase_input.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.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 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 /// soon as [isDirty] flips from `true` to `false`. 84 /// soon as [isDirty] flips from `true` to `false`.
85 Stream get onDone => _onDoneController.stream; 85 Stream get onDone => _onDoneController.stream;
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 =
95 new StreamController<AssetNode>.broadcast(sync: true);
95 96
96 /// Whether [this] is dirty and still has more processing to do. 97 /// Whether [this] is dirty and still has more processing to do.
97 /// 98 ///
98 /// A phase is considered dirty if any of the previous phases in the same 99 /// 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 /// cascade are dirty, since those phases could emit an asset that this phase
100 /// will then need to process. 101 /// will then need to process.
101 bool get isDirty => (_previous != null && _previous.isDirty) || 102 bool get isDirty => (_previous != null && _previous.isDirty) ||
102 _inputs.values.any((input) => input.isDirty) || 103 _inputs.values.any((input) => input.isDirty) ||
103 _groups.values.any((group) => group.isDirty); 104 _groups.values.any((group) => group.isDirty);
104 105
105 /// A stream that emits an event whenever any transforms in this phase logs 106 /// A stream that emits an event whenever any transforms in this phase logs
106 /// an entry. 107 /// an entry.
107 Stream<LogEntry> get onLog => _onLogPool.stream; 108 Stream<LogEntry> get onLog => _onLogPool.stream;
108 final _onLogPool = new StreamPool<LogEntry>.broadcast(); 109 final _onLogPool = new StreamPool<LogEntry>.broadcast();
109 110
110 /// The previous phase in the cascade, or null if this is the first phase. 111 /// The previous phase in the cascade, or null if this is the first phase.
111 final Phase _previous; 112 final Phase _previous;
112 113
113 /// The subscription to [_previous]'s [onDone] stream. 114 /// The subscription to [_previous]'s [onDone] stream.
114 StreamSubscription _previousOnDoneSubscription; 115 StreamSubscription _previousOnDoneSubscription;
115 116
116 /// The phase after this one. 117 /// The subscription to [_previous]'s [onAsset] stream.
117 /// 118 StreamSubscription<AssetNode> _previousOnAssetSubscription;
118 /// Outputs from this phase will be passed to it.
119 Phase get next => _next;
120 Phase _next;
121 119
122 /// A map of asset ids to completers for [getInput] requests. 120 /// A map of asset ids to completers for [getInput] requests.
123 /// 121 ///
124 /// If an asset node is requested before it's available, we put a completer in 122 /// If an asset node is requested before it's available, we put a completer in
125 /// this map to wait for the asset to be generated. If it's not generated, the 123 /// this map to wait for the asset to be generated. If it's not generated, the
126 /// completer should complete to `null`. 124 /// completer should complete to `null`.
127 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); 125 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>();
128 126
129 /// Returns all currently-available output assets for this phase. 127 /// Returns all currently-available output assets for this phase.
130 Set<AssetNode> get availableOutputs { 128 Set<AssetNode> get availableOutputs {
131 return _outputs.values 129 return _outputs.values
132 .map((output) => output.output) 130 .map((output) => output.output)
133 .where((node) => node.state.isAvailable) 131 .where((node) => node.state.isAvailable)
134 .toSet(); 132 .toSet();
135 } 133 }
136 134
137 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, 135 // TODO(nweiz): Rather than passing the cascade and the phase everywhere,
138 // create an interface that just exposes [getInput]. Emit errors via 136 // create an interface that just exposes [getInput]. Emit errors via
139 // [AssetNode]s. 137 // [AssetNode]s.
140 Phase(AssetCascade cascade, String location) 138 Phase(AssetCascade cascade, String location)
141 : this._(cascade, location, 0); 139 : this._(cascade, location, 0);
142 140
143 Phase._(this.cascade, this._location, this._index, [this._previous]) { 141 Phase._(this.cascade, this._location, this._index, [this._previous]) {
144 if (_previous != null) { 142 if (_previous != null) {
143 _previousOnAssetSubscription = _previous.onAsset.listen(addInput);
145 _previousOnDoneSubscription = _previous.onDone.listen((_) { 144 _previousOnDoneSubscription = _previous.onDone.listen((_) {
146 if (!isDirty) _onDoneController.add(null); 145 if (!isDirty) _onDoneController.add(null);
147 }); 146 });
148 } 147 }
149 148
150 onDone.listen((_) { 149 onDone.listen((_) {
151 // All the previous phases have finished building. If anyone's still 150 // 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, 151 // waiting for outputs, cut off the wait; we won't be generating them,
153 // at least until a source asset changes. 152 // at least until a source asset changes.
154 for (var completer in _pendingOutputRequests.values) { 153 for (var completer in _pendingOutputRequests.values) {
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 313
315 for (var input in _inputs.values) { 314 for (var input in _inputs.values) {
316 input.forceAllTransforms(); 315 input.forceAllTransforms();
317 } 316 }
318 } 317 }
319 318
320 /// Add a new phase after this one. 319 /// Add a new phase after this one.
321 /// 320 ///
322 /// This may only be called on a phase with no phase following it. 321 /// This may only be called on a phase with no phase following it.
323 Phase addPhase() { 322 Phase addPhase() {
324 assert(_next == null); 323 var next = new Phase._(cascade, _location, _index + 1, this);
325 _next = new Phase._(cascade, _location, _index + 1, this);
326 for (var output in _outputs.values.toList()) { 324 for (var output in _outputs.values.toList()) {
327 // Remove [output]'s listeners because now they should get the asset from 325 // Remove [output]'s listeners because now they should get the asset from
328 // [_next], rather than this phase. Any transforms consuming [output] will 326 // [next], rather than this phase. Any transforms consuming [output] will
329 // be re-run and will consume the output from the new final phase. 327 // be re-run and will consume the output from the new final phase.
330 output.removeListeners(); 328 output.removeListeners();
331 } 329 }
332 return _next; 330 return next;
333 } 331 }
334 332
335 /// Mark this phase as removed. 333 /// Mark this phase as removed.
336 /// 334 ///
337 /// This will remove all the phase's outputs and all following phases. 335 /// This will remove all the phase's outputs.
338 void remove() { 336 void remove() {
339 if (_previous != null) _previous._next = null;
340 removeFollowing();
341 for (var input in _inputs.values.toList()) { 337 for (var input in _inputs.values.toList()) {
342 input.remove(); 338 input.remove();
343 } 339 }
344 for (var group in _groups.values) { 340 for (var group in _groups.values) {
345 group.remove(); 341 group.remove();
346 } 342 }
347 _onAssetController.close(); 343 _onAssetController.close();
348 _onLogPool.close(); 344 _onLogPool.close();
349 if (_previousOnDoneSubscription != null) { 345 if (_previousOnDoneSubscription != null) {
350 _previousOnDoneSubscription.cancel(); 346 _previousOnDoneSubscription.cancel();
351 } 347 }
352 } 348 if (_previousOnAssetSubscription != null) {
353 349 _previousOnAssetSubscription.cancel();
354 /// Remove all phases after this one. 350 }
355 void removeFollowing() {
356 if (_next == null) return;
357 _next.remove();
358 _next = null;
359 } 351 }
360 352
361 /// Add [asset] as an output of this phase. 353 /// Add [asset] as an output of this phase.
362 void _handleOutput(AssetNode asset) { 354 void _handleOutput(AssetNode asset) {
363 if (_inputOrigins.contains(asset.origin)) { 355 if (_inputOrigins.contains(asset.origin)) {
364 _forwarders[asset.id].addIntermediateAsset(asset); 356 _forwarders[asset.id].addIntermediateAsset(asset);
365 } else { 357 } else {
366 _handleOutputWithoutForwarder(asset); 358 _handleOutputWithoutForwarder(asset);
367 } 359 }
368 } 360 }
(...skipping 12 matching lines...) Expand all
381 373
382 var exception = _outputs[asset.id].collisionException; 374 var exception = _outputs[asset.id].collisionException;
383 if (exception != null) cascade.reportError(exception); 375 if (exception != null) cascade.reportError(exception);
384 } 376 }
385 377
386 /// Emit [asset] as an output of this phase. 378 /// Emit [asset] as an output of this phase.
387 /// 379 ///
388 /// This should be called after [_handleOutput], so that collisions are 380 /// This should be called after [_handleOutput], so that collisions are
389 /// resolved. 381 /// resolved.
390 void _emit(AssetNode asset) { 382 void _emit(AssetNode asset) {
391 if (_next != null) { 383 _onAssetController.add(asset);
392 _next.addInput(asset);
393 } else {
394 _onAssetController.add(asset);
395 }
396 _providePendingAsset(asset); 384 _providePendingAsset(asset);
397 } 385 }
398 386
399 /// Provide an asset to a pending [getOutput] call. 387 /// Provide an asset to a pending [getOutput] call.
400 void _providePendingAsset(AssetNode asset) { 388 void _providePendingAsset(AssetNode asset) {
401 // If anyone's waiting for this asset, provide it to them. 389 // If anyone's waiting for this asset, provide it to them.
402 var request = _pendingOutputRequests.remove(asset.id); 390 var request = _pendingOutputRequests.remove(asset.id);
403 if (request == null) return; 391 if (request == null) return;
404 392
405 if (asset.state.isAvailable) { 393 if (asset.state.isAvailable) {
406 request.complete(asset); 394 request.complete(asset);
407 return; 395 return;
408 } 396 }
409 397
410 // A lazy asset may be emitted while still dirty. If so, we wait until it's 398 // A lazy asset may be emitted while still dirty. If so, we wait until it's
411 // either available or removed before trying again to access it. 399 // either available or removed before trying again to access it.
412 assert(asset.state.isDirty); 400 assert(asset.state.isDirty);
413 asset.force(); 401 asset.force();
414 asset.whenStateChanges().then((state) { 402 asset.whenStateChanges().then((state) {
415 if (state.isRemoved) return getOutput(asset.id); 403 if (state.isRemoved) return getOutput(asset.id);
416 return asset; 404 return asset;
417 }).then(request.complete).catchError(request.completeError); 405 }).then(request.complete).catchError(request.completeError);
418 } 406 }
419 407
420 String toString() => "phase $_location.$_index"; 408 String toString() => "phase $_location.$_index";
421 } 409 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/group_runner.dart ('k') | pkg/barback/lib/src/phase_input.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698