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

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

Issue 21275003: Move barback to a more event-based model. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Code review changes. Created 7 years, 4 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/asset_cascade.dart ('k') | pkg/barback/lib/src/package_provider.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.asset_node; 5 library barback.asset_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';
11 import 'errors.dart';
11 import 'phase.dart'; 12 import 'phase.dart';
12 import 'transform_node.dart'; 13 import 'transform_node.dart';
13 14
14 /// Describes an asset and its relationship to the build dependency graph. 15 /// Describes the current state of an asset as part of a transformation graph.
15 /// 16 ///
16 /// Keeps a cache of the last asset that was built for this node (i.e. for this 17 /// An asset node can be in one of three states (see [AssetState]). It provides
17 /// node's ID and phase) and tracks which transforms depend on it. 18 /// an [onStateChange] stream that emits an event whenever it changes state.
19 ///
20 /// Asset nodes are controlled using [AssetNodeController]s.
18 class AssetNode { 21 class AssetNode {
19 Asset asset; 22 /// The id of the asset that this node represents.
23 final AssetId id;
20 24
21 /// The [TransformNode]s that consume this node's asset as an input. 25 /// The current state of the asset node.
22 final consumers = new Set<TransformNode>(); 26 AssetState get state => _state;
27 AssetState _state;
23 28
24 AssetId get id => asset.id; 29 /// The concrete asset that this node represents.
30 ///
31 /// This is null unless [state] is [AssetState.AVAILABLE].
32 Asset get asset => _asset;
33 Asset _asset;
25 34
26 AssetNode(this.asset); 35 /// A broadcast stream that emits an event whenever the node changes state.
36 ///
37 /// This stream is synchronous to ensure that when a source asset is modified
38 /// or removed, the appropriate portion of the asset graph is dirtied before
39 /// any [Barback.getAssetById] calls emit newly-incorrect values.
40 Stream<AssetState> get onStateChange => _stateChangeController.stream;
27 41
28 /// Updates this node's generated asset value and marks all transforms that 42 /// This is synchronous so that a source being updated will always be
29 /// use this as dirty. 43 /// propagated through the build graph before anything that depends on it is
30 void updateAsset(Asset asset) { 44 /// requested.
31 // Cannot update an asset to one with a different ID. 45 final _stateChangeController =
32 assert(id == asset.id); 46 new StreamController<AssetState>.broadcast(sync: true);
33 47
34 this.asset = asset; 48 /// Returns a Future that completes when the node's asset is available.
35 consumers.forEach((consumer) => consumer.dirty()); 49 ///
50 /// If the asset is currently available, this completes synchronously to
51 /// ensure that the asset is still available in the [Future.then] callback.
52 ///
53 /// If the asset is removed before becoming available, this will throw an
54 /// [AssetNotFoundException].
55 Future<Asset> get whenAvailable {
56 return _waitForState((state) => state.isAvailable || state.isRemoved)
57 .then((state) {
58 if (state.isRemoved) throw new AssetNotFoundException(id);
59 return asset;
60 });
61 }
62
63 /// Returns a Future that completes when the node's asset is removed.
64 ///
65 /// If the asset is already removed when this is called, it completes
66 /// synchronously.
67 Future get whenRemoved => _waitForState((state) => state.isRemoved);
68
69 /// Runs [callback] repeatedly until the node's asset has maintained the same
70 /// value for the duration.
71 ///
72 /// This will run [callback] as soon as the asset is available (synchronously
73 /// if it's available immediately). If the [state] changes at all while
74 /// waiting for the Future returned by [callback] to complete, it will be
75 /// re-run as soon as it completes and the asset is available again. This will
76 /// continue until [state] doesn't change at all.
77 ///
78 /// If this asset is removed, this will throw an [AssetNotFoundException] as
79 /// soon as [callback]'s Future is finished running.
80 Future tryUntilStable(Future callback(Asset asset)) {
81 return whenAvailable.then((asset) {
82 var modifiedDuringCallback = false;
83 var subscription;
84 subscription = onStateChange.listen((_) {
85 modifiedDuringCallback = true;
86 subscription.cancel();
87 });
88
89 return callback(asset).then((result) {
90 subscription.cancel();
91
92 // If the asset was modified at all while running the callback, the
93 // result was invalid and we should try again.
94 if (modifiedDuringCallback) return tryUntilStable(callback);
95 return result;
96 });
97 });
98 }
99
100 /// Returns a Future that completes as soon as the node is in a state that
101 /// matches [test].
102 ///
103 /// The Future completes synchronously if this is already in such a state.
104 Future<AssetState> _waitForState(bool test(AssetState state)) {
105 if (test(state)) return new Future.sync(() => state);
106 return onStateChange.firstWhere(test);
107 }
108
109 AssetNode._(this.id)
110 : _state = AssetState.DIRTY;
111
112 AssetNode._available(Asset asset)
113 : id = asset.id,
114 _asset = asset,
115 _state = AssetState.AVAILABLE;
116 }
117
118 /// The controller for an [AssetNode].
119 ///
120 /// This controls which state the node is in.
121 class AssetNodeController {
122 final AssetNode node;
123
124 /// Creates a controller for a dirty node.
125 AssetNodeController(AssetId id)
126 : node = new AssetNode._(id);
127
128 /// Creates a controller for an available node with the given concrete
129 /// [asset].
130 AssetNodeController.available(Asset asset)
131 : node = new AssetNode._available(asset);
132
133 /// Marks the node as [AssetState.DIRTY].
134 void setDirty() {
135 assert(node._state != AssetState.REMOVED);
136 node._state = AssetState.DIRTY;
137 node._asset = null;
138 node._stateChangeController.add(AssetState.DIRTY);
139 }
140
141 /// Marks the node as [AssetState.REMOVED].
142 ///
143 /// Once a node is marked as removed, it can't be marked as any other state.
144 /// If a new asset is created with the same id, it will get a new node.
145 void setRemoved() {
146 assert(node._state != AssetState.REMOVED);
147 node._state = AssetState.REMOVED;
148 node._asset = null;
149 node._stateChangeController.add(AssetState.REMOVED);
150 }
151
152 /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset].
153 ///
154 /// It's an error to mark an already-available node as available. It should be
155 /// marked as dirty first.
156 void setAvailable(Asset asset) {
157 assert(asset.id == node.id);
158 assert(node._state != AssetState.REMOVED);
159 assert(node._state != AssetState.AVAILABLE);
160 node._state = AssetState.AVAILABLE;
161 node._asset = asset;
162 node._stateChangeController.add(AssetState.AVAILABLE);
36 } 163 }
37 } 164 }
165
166 // TODO(nweiz): add an error state.
167 /// An enum of states that an [AssetNode] can be in.
168 class AssetState {
169 /// The node has a concrete asset loaded, available, and up-to-date. The asset
170 /// is accessible via [AssetNode.asset]. An asset can only be marked available
171 /// again from the [AssetState.DIRTY] state.
172 static final AVAILABLE = const AssetState._("available");
173
174 /// The asset is no longer available, possibly for good. A removed asset will
175 /// never enter another state.
176 static final REMOVED = const AssetState._("removed");
177
178 /// The asset will exist in the future (unless it's removed), but the concrete
179 /// asset is not yet available.
180 static final DIRTY = const AssetState._("dirty");
181
182 /// Whether this state is [AssetState.AVAILABLE].
183 bool get isAvailable => this == AssetState.AVAILABLE;
184
185 /// Whether this state is [AssetState.REMOVED].
186 bool get isRemoved => this == AssetState.REMOVED;
187
188 /// Whether this state is [AssetState.DIRTY].
189 bool get isDirty => this == AssetState.DIRTY;
190
191 final String name;
192
193 const AssetState._(this.name);
194
195 String toString() => name;
196 }
OLDNEW
« no previous file with comments | « pkg/barback/lib/src/asset_cascade.dart ('k') | pkg/barback/lib/src/package_provider.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698