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

Unified Diff: pkg/barback/lib/src/phase.dart

Issue 25376003: Add support for transformer clusters to barback. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Expose the Operator class. Created 7 years, 3 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 side-by-side diff with in-line comments
Download patch
Index: pkg/barback/lib/src/phase.dart
diff --git a/pkg/barback/lib/src/phase.dart b/pkg/barback/lib/src/phase.dart
index 1230bea094e36970d402547ec6b386c868c33ec0..dbafc87d7f00b5721cc7cf98244906cd2a73ca4f 100644
--- a/pkg/barback/lib/src/phase.dart
+++ b/pkg/barback/lib/src/phase.dart
@@ -11,11 +11,15 @@ import 'asset_cascade.dart';
import 'asset_id.dart';
import 'asset_node.dart';
import 'asset_set.dart';
+import 'cluster_runner.dart';
import 'errors.dart';
+import 'operator.dart';
+import 'phase_forwarder.dart';
import 'phase_input.dart';
import 'phase_output.dart';
import 'stream_pool.dart';
import 'transformer.dart';
+import 'transformer_cluster.dart';
import 'utils.dart';
/// One phase in the ordered series of transformations in an [AssetCascade].
@@ -39,15 +43,38 @@ class Phase {
/// Their outputs will be available to the next phase.
final Set<Transformer> _transformers;
+ /// The clusters for this phase.
+ final _clusters = new Map<TransformerCluster, ClusterRunner>();
+
/// The inputs for this phase.
///
/// For the first phase, these will be the source assets. For all other
/// phases, they will be the outputs from the previous phase.
final _inputs = new Map<AssetId, PhaseInput>();
+ /// The forwarders for this phase.
+ final _forwarders = new Map<AssetId, PhaseForwarder>();
+
/// The outputs for this phase.
final _outputs = new Map<AssetId, PhaseOutput>();
+ // TODO(nweiz): don't re-calculate this on the fly all the time
Bob Nystrom 2013/10/04 21:47:50 "." and capitalize "Don't".
nweiz 2013/10/07 23:21:31 Done.
+ /// The set of all [AssetNode.origin] properties of the input assets for this
+ /// phase.
+ ///
+ /// This is used to determine which assets have been passed unmodified through
+ /// [_inputs] or [_clusters]. Eeach input asset has a PhaseInput in [_inputs].
Bob Nystrom 2013/10/04 21:47:50 "Eeach" -> "Each".
nweiz 2013/10/07 23:21:31 Done.
+ /// If that input isn't consumed by any transformers, it will be forwarded
+ /// through the PhaseInput. However, it's possible that it was consumed by a
+ /// cluster, and so shouldn't be forwarded through the phase as a whole.
+ ///
+ /// In order to detect whether an output has been forwarded through a cluster
+ /// or a PhaseInput, we must be able to distinguish it from other outputs with
+ /// the same id. To do so, we check if its origin is in [_inputOrigins]. If
+ /// so, it's been forwarded unmodified.
+ Set<AssetNode> get _inputOrigins =>
+ _inputs.values.map((input) => input.input.origin).toSet();
+
/// A stream that emits an event whenever this phase becomes dirty and needs
/// to be run.
///
@@ -62,6 +89,10 @@ class Phase {
/// This is used whenever an input is added or transforms are changed.
final _onDirtyController = new StreamController.broadcast(sync: true);
+ /// Whether this phase is dirty and needs to be run.
+ bool get isDirty => _inputs.values.any((input) => input.isDirty) ||
+ _clusters.values.any((cluster) => cluster.isDirty);
+
/// The phase after this one.
///
/// Outputs from this phase will be passed to it.
@@ -69,16 +100,21 @@ class Phase {
Phase _next;
/// Returns all currently-available output assets for this phase.
- AssetSet get availableOutputs {
- return new AssetSet.from(_outputs.values
+ Set<AssetNode> get availableOutputs {
+ return _outputs.values
.map((output) => output.output)
.where((node) => node.state.isAvailable)
- .map((node) => node.asset));
+ .toSet();
}
- Phase(this.cascade, Iterable<Transformer> transformers)
- : _transformers = transformers.toSet() {
+ Phase(this.cascade, Iterable<Operator> operators)
Bob Nystrom 2013/10/04 21:47:50 We could keep this API typed if the transformers a
nweiz 2013/10/07 23:21:31 I think that would result in more code duplication
+ : _transformers = operators.where((op) => op is Transformer).toSet() {
_onDirtyPool.add(_onDirtyController.stream);
+
+ for (var cluster in operators.where((op) => op is TransformerCluster)) {
+ _clusters[cluster] = new ClusterRunner(cascade, cluster);
+ _onDirtyPool.add(_clusters[cluster].onDirty);
+ }
}
/// Adds a new asset as an input for this phase.
@@ -94,11 +130,27 @@ class Phase {
void addInput(AssetNode node) {
if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
+ var forwarder = new PhaseForwarder(_clusters.length + 1);
Bob Nystrom 2013/10/04 21:47:50 Explain what the .length + 1 represents.
nweiz 2013/10/07 23:21:31 Done.
+ _forwarders[node.id] = forwarder;
+ forwarder.onForwarding.listen((asset) {
+ _addOutput(asset);
+
+ var exception = _outputs[asset.id].collisionException;
+ if (exception != null) cascade.reportError(exception);
+ });
+
var input = new PhaseInput(this, node, _transformers);
_inputs[node.id] = input;
- input.input.whenRemoved.then((_) => _inputs.remove(node.id));
+ input.input.whenRemoved.then((_) {
+ _inputs.remove(node.id);
+ _forwarders.remove(node.id).remove();
+ });
_onDirtyPool.add(input.onDirty);
_onDirtyController.add(null);
+
+ for (var cluster in _clusters.values) {
+ cluster.addInput(node);
+ }
}
/// Gets the asset node for an input [id].
@@ -123,22 +175,42 @@ class Phase {
});
}
- /// Set this phase's transformers to [transformers].
- void updateTransformers(Iterable<Transformer> transformers) {
+ /// Set this phase's operators to [operators].
+ void updateOperators(Iterable<Operator> operators) {
_onDirtyController.add(null);
+
+ var transformers = operators.where((op) => op is Transformer);
_transformers.clear();
_transformers.addAll(transformers);
for (var input in _inputs.values) {
- input.updateTransformers(_transformers);
+ input.updateTransformers(transformers);
+ }
+
+ var newClusters = operators.where((op) => op is TransformerCluster).toSet();
+ var oldClusters = _clusters.keys.toSet();
+ for (var removed in oldClusters.difference(newClusters)) {
+ _clusters.remove(removed).remove();
+ }
Bob Nystrom 2013/10/04 21:47:50 Nit but add blank line after "}".
nweiz 2013/10/07 23:21:31 Done.
+ for (var added in newClusters.difference(oldClusters)) {
+ var runner = new ClusterRunner(cascade, added);
+ _clusters[added] = runner;
+ _onDirtyPool.add(runner.onDirty);
+ for (var input in _inputs.values) {
+ runner.addInput(input.input);
+ }
+ }
+
+ for (var forwarder in _forwarders.values) {
+ forwarder.channels = _clusters.length + 1;
}
}
- /// Add a new phase after this one with [transformers].
+ /// Add a new phase after this one with [operators].
///
/// This may only be called on a phase with no phase following it.
- Phase addPhase(Iterable<Transformer> transformers) {
+ Phase addPhase(Iterable<Operators> operators) {
assert(_next == null);
- _next = new Phase(cascade, transformers);
+ _next = new Phase(cascade, operators);
for (var output in _outputs.values.toList()) {
// Remove [output]'s listeners because now they should get the asset from
// [_next], rather than this phase. Any transforms consuming [output] will
@@ -162,6 +234,9 @@ class Phase {
for (var input in _inputs.values.toList()) {
input.remove();
}
+ for (var cluster in _clusters.values) {
+ cluster.remove();
+ }
_onDirtyPool.close();
}
@@ -177,26 +252,32 @@ class Phase {
/// Returns a future that completes when processing is done. If there is
/// nothing to process, returns `null`.
Future process() {
- if (!_inputs.values.any((input) => input.isDirty)) return null;
+ if (!isDirty) return null;
var outputIds = new Set<AssetId>();
- return Future.wait(_inputs.values.map((input) {
- if (!input.isDirty) return new Future.value(new Set());
- return input.process().then((outputs) {
- for (var asset in outputs) {
- outputIds.add(asset.id);
- if (_outputs.containsKey(asset.id)) {
- _outputs[asset.id].add(asset);
- } else {
- _outputs[asset.id] = new PhaseOutput(this, asset);
- _outputs[asset.id].output.whenRemoved.then((_) {
- _outputs.remove(asset.id);
- });
- if (_next != null) _next.addInput(_outputs[asset.id].output);
- }
+ void _handleOutputs(Set<AssetNode> outputs) {
+ for (var asset in outputs) {
+ if (_inputOrigins.contains(asset.origin)) {
+ _forwarders[asset.id].add(asset);
+ continue;
}
- });
- })).then((_) {
+
+ outputIds.add(asset.id);
+ _addOutput(asset);
+ }
+ }
+
+ var outputFutures = []..addAll(_inputs.values.map((input) {
+ if (!input.isDirty) return new Future.value(new Set());
+ return input.process().then(_handleOutputs);
+ }))..addAll(_clusters.values.map((input) {
Bob Nystrom 2013/10/04 21:47:50 Maybe it's just me, but using ".." here feels a bi
nweiz 2013/10/07 23:21:31 Done.
+ if (!input.isDirty) return new Future.value(new Set());
+ return input.process().then(_handleOutputs);
+ }));
+
+ // TODO(nweiz): handle pass-through.
+
+ return Future.wait(outputFutures).then((_) {
// Report collisions in a deterministic order.
outputIds = outputIds.toList();
outputIds.sort((a, b) => a.compareTo(b));
@@ -209,4 +290,17 @@ class Phase {
}
});
}
+
+ /// Add [asset] as an output of this phase.
+ void _addOutput(AssetNode asset) {
+ if (_outputs.containsKey(asset.id)) {
+ _outputs[asset.id].add(asset);
+ } else {
+ _outputs[asset.id] = new PhaseOutput(this, asset);
+ _outputs[asset.id].output.whenRemoved.then((_) {
+ _outputs.remove(asset.id);
+ });
+ if (_next != null) _next.addInput(_outputs[asset.id].output);
+ }
+ }
}

Powered by Google App Engine
This is Rietveld 408576698