Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 = new StreamController<AssetNode>(sync: true); |
| 95 | 95 |
| 96 /// Whether [this] is dirty and still has more processing to do. | 96 /// Whether [this] is dirty and still has more processing to do. |
|
Bob Nystrom
2014/03/07 17:05:58
Add a bit here explaining that a phase is if it's
nweiz
2014/03/07 19:39:58
Done.
| |
| 97 bool get isDirty => _inputs.values.any((input) => input.isDirty) || | 97 bool get isDirty => (_previous != null && _previous.isDirty) || |
| 98 _inputs.values.any((input) => input.isDirty) || | |
| 98 _groups.values.any((group) => group.isDirty); | 99 _groups.values.any((group) => group.isDirty); |
| 99 | 100 |
| 100 /// Whether [this] or any previous phase is dirty. | |
| 101 bool get _isTransitivelyDirty => isDirty || | |
| 102 (_previous != null && _previous._isTransitivelyDirty); | |
| 103 | |
| 104 /// A stream that emits an event whenever any transforms in this phase logs | 101 /// A stream that emits an event whenever any transforms in this phase logs |
| 105 /// an entry. | 102 /// an entry. |
| 106 Stream<LogEntry> get onLog => _onLogPool.stream; | 103 Stream<LogEntry> get onLog => _onLogPool.stream; |
| 107 final _onLogPool = new StreamPool<LogEntry>.broadcast(); | 104 final _onLogPool = new StreamPool<LogEntry>.broadcast(); |
| 108 | 105 |
| 109 /// The previous phase in the cascade, or null if this is the first phase. | 106 /// The previous phase in the cascade, or null if this is the first phase. |
| 110 final Phase _previous; | 107 final Phase _previous; |
| 111 | 108 |
| 109 /// The subscription to [_previous]'s [onDone] stream. | |
| 110 StreamSubscription _previousSubscription; | |
|
Bob Nystrom
2014/03/07 17:05:58
_previousOnDoneSubscription?
nweiz
2014/03/07 19:39:58
Done.
| |
| 111 | |
| 112 /// The phase after this one. | 112 /// The phase after this one. |
| 113 /// | 113 /// |
| 114 /// Outputs from this phase will be passed to it. | 114 /// Outputs from this phase will be passed to it. |
| 115 Phase get next => _next; | 115 Phase get next => _next; |
| 116 Phase _next; | 116 Phase _next; |
| 117 | 117 |
| 118 /// A map of asset ids to completers for [getInput] requests. | 118 /// A map of asset ids to completers for [getInput] requests. |
| 119 /// | 119 /// |
| 120 /// If an asset node is requested before it's available, we put a completer in | 120 /// If an asset node is requested before it's available, we put a completer in |
| 121 /// this map to wait for the asset to be generated. If it's not generated, the | 121 /// this map to wait for the asset to be generated. If it's not generated, the |
| 122 /// completer should complete to `null`. | 122 /// completer should complete to `null`. |
| 123 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); | 123 final _pendingOutputRequests = new Map<AssetId, Completer<AssetNode>>(); |
| 124 | 124 |
| 125 /// Returns all currently-available output assets for this phase. | 125 /// Returns all currently-available output assets for this phase. |
| 126 Set<AssetNode> get availableOutputs { | 126 Set<AssetNode> get availableOutputs { |
| 127 return _outputs.values | 127 return _outputs.values |
| 128 .map((output) => output.output) | 128 .map((output) => output.output) |
| 129 .where((node) => node.state.isAvailable) | 129 .where((node) => node.state.isAvailable) |
| 130 .toSet(); | 130 .toSet(); |
| 131 } | 131 } |
| 132 | 132 |
| 133 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, | 133 // TODO(nweiz): Rather than passing the cascade and the phase everywhere, |
| 134 // create an interface that just exposes [getInput]. Emit errors via | 134 // create an interface that just exposes [getInput]. Emit errors via |
| 135 // [AssetNode]s. | 135 // [AssetNode]s. |
| 136 Phase(AssetCascade cascade, String location) | 136 Phase(AssetCascade cascade, String location) |
| 137 : this._(cascade, location, 0); | 137 : this._(cascade, location, 0); |
| 138 | 138 |
| 139 Phase._(this.cascade, this._location, this._index, [this._previous]) { | 139 Phase._(this.cascade, this._location, this._index, [this._previous]) { |
| 140 // TODO(nweiz): This does O(n^2) work whenever a phase emits an [onDone] | 140 if (_previous != null) { |
| 141 // event, since each phase after it has to check each phase before. Find a | 141 _previousSubscription = _previous.onDone.listen((_) { |
| 142 // better way to do this. | 142 if (!isDirty) _onDoneController.add(null); |
| 143 for (var phase = this; phase != null; phase = phase._previous) { | |
| 144 phase.onDone.listen((_) { | |
| 145 if (_isTransitivelyDirty) return; | |
| 146 | |
| 147 // All the previous phases have finished building. If anyone's still | |
| 148 // waiting for outputs, cut off the wait; we won't be generating them, | |
| 149 // at least until a source asset changes. | |
| 150 for (var completer in _pendingOutputRequests.values) { | |
| 151 completer.complete(null); | |
| 152 } | |
| 153 _pendingOutputRequests.clear(); | |
| 154 }); | 143 }); |
| 155 } | 144 } |
| 145 | |
| 146 this.onDone.listen((_) { | |
|
Bob Nystrom
2014/03/07 17:05:58
Remove "this.".
nweiz
2014/03/07 19:39:58
Done.
| |
| 147 // All the previous phases have finished building. If anyone's still | |
| 148 // waiting for outputs, cut off the wait; we won't be generating them, | |
| 149 // at least until a source asset changes. | |
| 150 for (var completer in _pendingOutputRequests.values) { | |
| 151 completer.complete(null); | |
| 152 } | |
| 153 _pendingOutputRequests.clear(); | |
| 154 }); | |
| 156 } | 155 } |
| 157 | 156 |
| 158 /// Adds a new asset as an input for this phase. | 157 /// Adds a new asset as an input for this phase. |
| 159 /// | 158 /// |
| 160 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase | 159 /// [node] doesn't have to be [AssetState.AVAILABLE]. Once it is, the phase |
| 161 /// will automatically begin determining which transforms can consume it as a | 160 /// will automatically begin determining which transforms can consume it as a |
| 162 /// primary input. The transforms themselves won't be applied until [process] | 161 /// primary input. The transforms themselves won't be applied until [process] |
| 163 /// is called, however. | 162 /// is called, however. |
| 164 /// | 163 /// |
| 165 /// This should only be used for brand-new assets or assets that have been | 164 /// This should only be used for brand-new assets or assets that have been |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 243 // try again, since it could be generated again. | 242 // try again, since it could be generated again. |
| 244 output.force(); | 243 output.force(); |
| 245 return output.whenAvailable((_) => output).catchError((error) { | 244 return output.whenAvailable((_) => output).catchError((error) { |
| 246 if (error is! AssetNotFoundException) throw error; | 245 if (error is! AssetNotFoundException) throw error; |
| 247 return getOutput(id); | 246 return getOutput(id); |
| 248 }); | 247 }); |
| 249 } | 248 } |
| 250 | 249 |
| 251 // If neither this phase nor the previous phases are dirty, the requested | 250 // If neither this phase nor the previous phases are dirty, the requested |
| 252 // output won't be generated and we can safely return null. | 251 // output won't be generated and we can safely return null. |
| 253 if (!_isTransitivelyDirty) return null; | 252 if (!isDirty) return null; |
| 254 | 253 |
| 255 // Otherwise, store a completer for the asset node. If it's generated in | 254 // Otherwise, store a completer for the asset node. If it's generated in |
| 256 // the future, we'll complete this completer. | 255 // the future, we'll complete this completer. |
| 257 var completer = _pendingOutputRequests.putIfAbsent(id, | 256 var completer = _pendingOutputRequests.putIfAbsent(id, |
| 258 () => new Completer.sync()); | 257 () => new Completer.sync()); |
| 259 return completer.future; | 258 return completer.future; |
| 260 }); | 259 }); |
| 261 } | 260 } |
| 262 | 261 |
| 263 /// Set this phase's transformers to [transformers]. | 262 /// Set this phase's transformers to [transformers]. |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 328 _previous._next = null; | 327 _previous._next = null; |
| 329 removeFollowing(); | 328 removeFollowing(); |
| 330 for (var input in _inputs.values.toList()) { | 329 for (var input in _inputs.values.toList()) { |
| 331 input.remove(); | 330 input.remove(); |
| 332 } | 331 } |
| 333 for (var group in _groups.values) { | 332 for (var group in _groups.values) { |
| 334 group.remove(); | 333 group.remove(); |
| 335 } | 334 } |
| 336 _onAssetController.close(); | 335 _onAssetController.close(); |
| 337 _onLogPool.close(); | 336 _onLogPool.close(); |
| 337 _previousSubscription.cancel(); | |
| 338 } | 338 } |
| 339 | 339 |
| 340 /// Remove all phases after this one. | 340 /// Remove all phases after this one. |
| 341 void removeFollowing() { | 341 void removeFollowing() { |
| 342 if (_next == null) return; | 342 if (_next == null) return; |
| 343 _next.remove(); | 343 _next.remove(); |
| 344 _next = null; | 344 _next = null; |
| 345 } | 345 } |
| 346 | 346 |
| 347 /// Add [asset] as an output of this phase. | 347 /// Add [asset] as an output of this phase. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 assert(asset.state.isDirty); | 398 assert(asset.state.isDirty); |
| 399 asset.force(); | 399 asset.force(); |
| 400 asset.whenStateChanges().then((state) { | 400 asset.whenStateChanges().then((state) { |
| 401 if (state.isRemoved) return getOutput(asset.id); | 401 if (state.isRemoved) return getOutput(asset.id); |
| 402 return asset; | 402 return asset; |
| 403 }).then(request.complete).catchError(request.completeError); | 403 }).then(request.complete).catchError(request.completeError); |
| 404 } | 404 } |
| 405 | 405 |
| 406 String toString() => "phase $_location.$_index"; | 406 String toString() => "phase $_location.$_index"; |
| 407 } | 407 } |
| OLD | NEW |