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

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: 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
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;
Bob Nystrom 2013/07/31 20:05:41 Is it an error to access this when not available?
nweiz 2013/07/31 22:47:53 I think it's reasonable to access it and check for
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);
Bob Nystrom 2013/07/31 20:05:41 Indent +2.
nweiz 2013/07/31 22:47:53 Done.
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 will complete 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 will complete
Bob Nystrom 2013/07/31 20:05:41 "will complete" -> "completes"
nweiz 2013/07/31 22:47:53 Done.
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 // If the asset was modified at all while running the callback, the
91 // result was invalid and we should try again.
92 if (modifiedDuringCallback) return tryUntilStable(callback);
93 subscription.cancel();
Bob Nystrom 2013/07/31 20:05:41 Move this above the if statement.
nweiz 2013/07/31 22:47:53 Done.
94 return result;
95 });
96 });
97 }
98
99 /// Returns a Future that completes as soon as the node is in a state that
100 /// matches [test].
101 ///
102 /// The Future will complete synchronously if this is already in such a state.
103 Future<AssetState> _waitForState(bool test(AssetState state)) {
104 if (test(state)) return new Future.sync(() => state);
105 return onStateChange.firstWhere(test);
106 }
107
108 AssetNode._(this.id)
109 : _state = AssetState.DIRTY;
110
111 AssetNode._available(Asset asset)
112 : id = asset.id,
113 _asset = asset,
114 _state = AssetState.AVAILABLE;
115 }
116
117 /// The controller for an [AssetNode].
118 ///
119 /// This controls which state the node is in.
120 class AssetNodeController {
121 final AssetNode node;
122
123 /// Creates a controller for a dirty node.
124 AssetNodeController(AssetId id)
125 : node = new AssetNode._(id);
126
127 /// Creates a controller for an available node with the given concrete
128 /// [asset].
129 AssetNodeController.available(Asset asset)
130 : node = new AssetNode._available(asset);
131
132 /// Marks the node as [AssetState.DIRTY].
133 void setDirty() {
134 assert(node._state != AssetState.REMOVED);
135 node._state = AssetState.DIRTY;
136 node._asset = null;
137 node._stateChangeController.add(AssetState.DIRTY);
138 }
139
140 /// Marks the node as [AssetState.REMOVED].
141 ///
142 /// Once a node is marked as removed, it can't be marked as any other state.
143 /// If a new asset is created with the same id, it should have a new node.
Bob Nystrom 2013/07/31 20:05:41 "should have" -> "will get"
nweiz 2013/07/31 22:47:53 Done.
144 void setRemoved() {
145 assert(node._state != AssetState.REMOVED);
146 node._state = AssetState.REMOVED;
147 node._asset = null;
148 node._stateChangeController.add(AssetState.REMOVED);
149 }
150
151 /// Marks the node as [AssetState.AVAILABLE] with the given concrete [asset].
152 ///
153 /// It's an error to mark an already-available node as available. It should be
154 /// marked as dirty first.
155 void setAvailable(Asset asset) {
156 assert(asset.id == node.id);
157 assert(node._state != AssetState.REMOVED);
158 assert(node._state != AssetState.AVAILABLE);
Bob Nystrom 2013/07/31 20:05:41 assert(node._state == AssetState.DIRTY);
nweiz 2013/07/31 22:47:53 I intentionally avoided that because I'm planning
159 node._state = AssetState.AVAILABLE;
160 node._asset = asset;
161 node._stateChangeController.add(AssetState.AVAILABLE);
36 } 162 }
37 } 163 }
164
165 // TODO(nweiz): add an error state.
166 /// An enum of states that an [AssetNode] can be in.
167 class AssetState {
168 /// The state of having an asset available.
Bob Nystrom 2013/07/31 20:05:41 This sentence is strange. Just ditch it and use th
nweiz 2013/07/31 22:47:53 Done.
169 ///
170 /// This state indicates that the node has a concrete asset loaded, available,
Bob Nystrom 2013/07/31 20:05:41 Remove "This state indicates that".
nweiz 2013/07/31 22:47:53 Done.
171 /// and up-to-date. The asset is accessible via [AssetNode.asset]. An
172 /// available asset may be marked available again, but first it will be marked
173 /// [AssetState.DIRTY].
Bob Nystrom 2013/07/31 20:05:41 This reads weird. Are you saying that an asset in
nweiz 2013/07/31 22:47:53 Done.
174 static final AVAILABLE = new AssetState._("available");
175
176 /// The state of being removed.
177 ///
178 /// This state indicates that the asset is no longer available, possibly for
179 /// good. A removed asset will never enter another state.
Bob Nystrom 2013/07/31 20:05:41 Same as above. Remove first sentence, use this one
nweiz 2013/07/31 22:47:53 Done.
180 static final REMOVED = new AssetState._("removed");
181
182 /// The state of waiting to be generated.
183 ///
184 /// This state indicates that the asset will exist in the future (unless it's
185 /// removed), but the concrete asset is not yet available.
Bob Nystrom 2013/07/31 20:05:41 Ditto.
nweiz 2013/07/31 22:47:53 Done.
186 static final DIRTY = new AssetState._("dirty");
Bob Nystrom 2013/07/31 20:05:41 Make these all constants instead of final, or name
nweiz 2013/07/31 22:47:53 Done.
187
188 /// Whether this state is [AssetState.AVAILABLE].
189 bool get isAvailable => this == AssetState.AVAILABLE;
190
191 /// Whether this state is [AssetState.REMOVED].
192 bool get isRemoved => this == AssetState.REMOVED;
193
194 /// Whether this state is [AssetState.DIRTY].
195 bool get isDirty => this == AssetState.DIRTY;
196
197 final String name;
198
199 AssetState._(this.name);
Bob Nystrom 2013/07/31 20:05:41 Make this a const constructor.
nweiz 2013/07/31 22:47:53 Done.
200
201 String toString() => name;
202 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698