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

Side by Side Diff: sdk/lib/async/future_impl.dart

Issue 23926011: Rewrite Futures. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Remove chained future cycle test. 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 part of dart.async; 5 part of dart.async;
6 6
7 /** The onValue and onError handlers return either a value or a future */
8 typedef dynamic _FutureOnValue<T>(T value);
9 typedef dynamic _FutureOnError(error);
10 /** Test used by [Future.catchError] to handle skip some errors. */
11 typedef bool _FutureErrorTest(var error);
12 /** Used by [WhenFuture]. */
13 typedef _FutureAction();
14
7 abstract class _Completer<T> implements Completer<T> { 15 abstract class _Completer<T> implements Completer<T> {
8 final Future<T> future; 16 final _Future<T> future = new _Future<T>();
9 bool _isComplete = false;
10 17
11 _Completer() : future = new _FutureImpl<T>() { 18 void complete([T value]);
12 _FutureImpl futureImpl = future;
13 futureImpl._zone.expectCallback();
14 }
15 19
16 void _setFutureValue(T value); 20 void completeError(Object error, [Object stackTrace = null]);
17 void _setFutureError(error); 21
22 // The future's _isComplete doesn't take into account pending completions.
23 // We therefore use _mayComplete.
24 bool get isCompleted => !future._mayComplete;
25 }
26
27 class _AsyncCompleter<T> extends _Completer<T> {
18 28
19 void complete([T value]) { 29 void complete([T value]) {
20 if (_isComplete) throw new StateError("Future already completed"); 30 future._asyncComplete(value);
21 _isComplete = true;
22 _FutureImpl futureImpl = future;
23 _setFutureValue(value);
24 } 31 }
25 32
26 void completeError(Object error, [Object stackTrace = null]) { 33 void completeError(Object error, [Object stackTrace = null]) {
27 if (_isComplete) throw new StateError("Future already completed"); 34 future._asyncCompleteError(error, stackTrace);
28 _isComplete = true;
29 if (stackTrace != null) {
30 // Force the stack trace onto the error, even if it already had one.
31 _attachStackTrace(error, stackTrace);
32 }
33 _FutureImpl futureImpl = future;
34 _setFutureError(error);
35 }
36
37 bool get isCompleted => _isComplete;
38 }
39
40 class _AsyncCompleter<T> extends _Completer<T> {
41 void _setFutureValue(T value) {
42 _FutureImpl future = this.future;
43 future._asyncSetValue(value);
44 // The async-error will schedule another callback, so we can cancel
45 // the expectation without shutting down the zone.
46 future._zone.cancelCallbackExpectation();
47 }
48
49 void _setFutureError(error) {
50 _FutureImpl future = this.future;
51 future._asyncSetError(error);
52 // The async-error will schedule another callback, so we can cancel
53 // the expectation without shutting down the zone.
54 future._zone.cancelCallbackExpectation();
55 } 35 }
56 } 36 }
57 37
58 class _SyncCompleter<T> extends _Completer<T> { 38 class _SyncCompleter<T> extends _Completer<T> {
59 void _setFutureValue(T value) { 39
60 _FutureImpl future = this.future; 40 void complete([T value]) {
61 future._setValue(value); 41 future._complete(value);
62 future._zone.cancelCallbackExpectation();
63 } 42 }
64 43
65 void _setFutureError(error) { 44 void completeError(Object error, [Object stackTrace = null]) {
66 _FutureImpl future = this.future; 45 future._completeError(error, stackTrace);
67 future._setError(error);
68 future._zone.cancelCallbackExpectation();
69 } 46 }
70 } 47 }
71 48
72 /** 49 class _Future<T> implements Future<T> {
73 * A listener on a future.
74 *
75 * When the future completes, the [_sendValue] or [_sendError] method
76 * is invoked with the result.
77 *
78 * Listeners are kept in a linked list.
79 */
80 abstract class _FutureListener<T> {
81 _FutureListener _nextListener;
82 factory _FutureListener.wrap(_FutureImpl future) {
83 return new _FutureListenerWrapper(future);
84 }
85 void _sendValue(T value);
86 void _sendError(error);
87
88 bool _inSameErrorZone(_Zone otherZone);
89 }
90
91 /** Adapter for a [_FutureImpl] to be a future result listener. */
92 class _FutureListenerWrapper<T> implements _FutureListener<T> {
93 _FutureImpl future;
94 _FutureListener _nextListener;
95 _FutureListenerWrapper(this.future);
96 _sendValue(T value) { future._setValueUnchecked(value); }
97 _sendError(error) { future._setErrorUnchecked(error); }
98 bool _inSameErrorZone(_Zone otherZone) => future._inSameErrorZone(otherZone);
99 }
100
101 /**
102 * This listener is installed at error-zone boundaries. It signals an
103 * uncaught error in the zone of origin when an error is sent from one error
104 * zone to another.
105 *
106 * When a Future is listening to another Future and they have not been
107 * instantiated in the same error-zone then Futures put an instance of this
108 * class between them (see [_FutureImpl._addListener]).
109 *
110 * For example:
111 *
112 * var completer = new Completer();
113 * var future = completer.future.then((x) => x);
114 * catchErrors(() {
115 * var future2 = future.catchError(print);
116 * });
117 * completer.completeError(499);
118 *
119 * In this example `future` and `future2` are in different error-zones. The
120 * error (499) that originates outside `catchErrors` must not reach the
121 * `catchError` future (`future2`) inside `catchErrors`.
122 *
123 * When invoking `catchError` on `future` the Future installs an
124 * [_ErrorZoneBoundaryListener] between itself and the result, `future2`.
125 *
126 * Conceptually _ErrorZoneBoundaryListeners could be implemented as
127 * `catchError`s on the origin future as well.
128 */
129 class _ErrorZoneBoundaryListener implements _FutureListener {
130 _FutureListener _nextListener;
131 final _FutureListener _listener;
132
133 _ErrorZoneBoundaryListener(this._listener);
134
135 bool _inSameErrorZone(_Zone otherZone) {
136 // Should never be called. We use [_inSameErrorZone] to know if we have
137 // to insert an instance of [_ErrorZoneBoundaryListener] (and in the
138 // controller). Once we have inserted one we should never need to use it
139 // anymore.
140 throw new UnsupportedError(
141 "A Zone boundary doesn't support the inSameErrorZone test.");
142 }
143
144 void _sendValue(value) {
145 _listener._sendValue(value);
146 }
147
148 void _sendError(error) {
149 // We are not allowed to send an error from one error-zone to another.
150 // This is the whole purpose of this class.
151 _Zone.current.handleUncaughtError(error);
152 }
153 }
154
155 class _FutureImpl<T> implements Future<T> {
156 // State of the future. The state determines the interpretation of the 50 // State of the future. The state determines the interpretation of the
157 // [resultOrListeners] field. 51 // [resultOrListeners] field.
158 // TODO(lrn): rename field since it can also contain a chained future. 52 // TODO(lrn): rename field since it can also contain a chained future.
159 53
160 /// Initial state, waiting for a result. In this state, the 54 /// Initial state, waiting for a result. In this state, the
161 /// [resultOrListeners] field holds a single-linked list of 55 /// [resultOrListeners] field holds a single-linked list of
162 /// [FutureListener] listeners. 56 /// [FutureListener] listeners.
163 static const int _INCOMPLETE = 0; 57 static const int _INCOMPLETE = 0;
164 /// Pending completion. Set when completed using [_asyncSetValue] or 58 /// Pending completion. Set when completed using [_asyncComplete] or
165 /// [_asyncSetError]. It is an error to try to complete it again. 59 /// [_asyncCompleteError]. It is an error to try to complete it again.
166 static const int _PENDING_COMPLETE = 1; 60 static const int _PENDING_COMPLETE = 1;
167 /// The future has been chained to another future. The result of that 61 /// The future has been chained to another future. The result of that
168 /// other future becomes the result of this future as well. 62 /// other future becomes the result of this future as well.
169 /// In this state, the [resultOrListeners] field holds the future that 63 /// In this state, no callback should be executed anymore.
170 /// will give the result to this future. Both existing and new listeners are 64 // TODO(floitsch): we don't really need a special "_CHAINED" state. We could
171 /// forwarded directly to the other future. 65 // just use the PENDING_COMPLETE state instead.
172 static const int _CHAINED = 2; 66 static const int _CHAINED = 2;
173 /// The future has been chained to another future, but there hasn't been
174 /// any listeners added to this future yet. If it is completed with an
175 /// error, the error will be considered unhandled.
176 static const int _CHAINED_UNLISTENED = 6;
177 /// The future has been completed with a value result. 67 /// The future has been completed with a value result.
178 static const int _VALUE = 8; 68 static const int _VALUE = 4;
179 /// The future has been completed with an error result. 69 /// The future has been completed with an error result.
180 static const int _ERROR = 12; 70 static const int _ERROR = 8;
181 71
182 /** Whether the future is complete, and as what. */ 72 /** Whether the future is complete, and as what. */
183 int _state = _INCOMPLETE; 73 int _state = _INCOMPLETE;
184 74
185 final _Zone _zone = _Zone.current.fork(); 75 final _Zone _zone = _Zone.current.fork();
186 76
187 bool get _isChained => (_state & _CHAINED) != 0; 77 bool get _mayComplete => _state == _INCOMPLETE;
188 bool get _hasChainedListener => _state == _CHAINED; 78 bool get _isChained => _state == _CHAINED;
189 bool get _isComplete => _state >= _VALUE; 79 bool get _isComplete => _state >= _VALUE;
190 bool get _mayComplete => _state == _INCOMPLETE;
191 bool get _hasValue => _state == _VALUE; 80 bool get _hasValue => _state == _VALUE;
192 bool get _hasError => _state >= _ERROR; 81 bool get _hasError => _state == _ERROR;
82
83 set _isChained(bool value) {
84 if (value) {
85 assert(_mayComplete);
86 _state = _CHAINED;
87 } else {
88 assert(_isChained);
89 _state = _INCOMPLETE;
90 }
91 }
193 92
194 /** 93 /**
195 * Either the result, a list of listeners or another future. 94 * Either the result, a list of listeners or another future.
196 * 95 *
197 * The result of the future is either a value or an error. 96 * The result of the future is either a value or an error.
198 * A result is only stored when the future has completed. 97 * A result is only stored when the future has completed.
199 * 98 *
200 * The listeners is an internally linked list of [_FutureListener]s. 99 * The listeners is an internally linked list of [_FutureListener]s.
201 * Listeners are only remembered while the future is not yet complete, 100 * Listeners are only remembered while the future is not yet complete,
202 * and it is not chained to another future. 101 * and it is not chained to another future.
203 * 102 *
204 * The future is another future that his future is chained to. This future 103 * The future is another future that his future is chained to. This future
205 * is waiting for the other future to complete, and when it does, this future 104 * is waiting for the other future to complete, and when it does, this future
206 * will complete with the same result. 105 * will complete with the same result.
207 * All listeners are forwarded to the other future. 106 * All listeners are forwarded to the other future.
208 * 107 *
209 * The cases are disjoint (incomplete and unchained, incomplete and 108 * The cases are disjoint (incomplete and unchained, incomplete and
210 * chained, or completed with value or error), so the field only needs to hold 109 * chained, or completed with value or error), so the field only needs to hold
211 * one value at a time. 110 * one value at a time.
212 */ 111 */
213 var _resultOrListeners; 112 var _resultOrListeners;
214 113
215 _FutureImpl(); 114 _Future _nextListener;
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Extra field, no documentation? What is this?
Lasse Reichstein Nielsen 2013/09/10 13:52:12 If a single listener is the common case, how about
floitsch 2013/09/10 17:19:14 The same as before (line 81). Will add documentati
floitsch 2013/09/10 17:19:14 Completely agree. Adding a TODO.
216 115
217 _FutureImpl.immediate(T value) { 116 // TODO(floitsch): we only need two closure fields to store the callbacks.
117 // If we store the type of a closure in the state field (where there are
118 // still bits left), we can just store two closures instead of using 4
119 // fields of which 2 are always null.
120 final _FutureOnValue _onValueCallback;
121 final _FutureErrorTest _errorTestCallback;
122 final _FutureOnError _onErrorCallback;
123 final _FutureAction _whenCompleteActionCallback;
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Four more fields? Even two? How about just one fie
Lasse Reichstein Nielsen 2013/09/10 13:52:12 I am concerned about having the extra overhead on
floitsch 2013/09/10 17:19:14 See TODO above. Will optimize later.
floitsch 2013/09/10 17:19:14 Agreed. We will have lots of time to optimize...
124
125 _FutureOnValue get _onValue => _isChained ? null : _onValueCallback;
126 _FutureErrorTest get _errorTest => _isChained ? null : _errorTestCallback;
127 _FutureOnError get _onError => _isChained ? null : _onErrorCallback;
128 _FutureAction get _whenCompleteAction
129 => _isChained ? null : _whenCompleteActionCallback;
130
131 _Future()
132 : _onValueCallback = null, _errorTestCallback = null,
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Default is null, you don't have to write all these
Lasse Reichstein Nielsen 2013/09/10 13:52:12 My bad.
floitsch 2013/09/10 17:19:14 That is not correct. Final fields need to be initi
133 _onErrorCallback = null, _whenCompleteActionCallback = null;
134
135 _Future.immediate(T value)
136 : _onValueCallback = null, _errorTestCallback = null,
137 _onErrorCallback = null, _whenCompleteActionCallback = null {
138 _asyncComplete(value);
139 }
140
141 _Future.immediateError(var error, [Object stackTrace])
142 : _onValueCallback = null, _errorTestCallback = null,
143 _onErrorCallback = null, _whenCompleteActionCallback = null {
144 _asyncCompleteError(error, stackTrace);
145 }
146
147 _Future._then(this._onValueCallback, this._onErrorCallback)
148 : _errorTestCallback = null, _whenCompleteActionCallback = null {
149 _zone.expectCallback();
150 }
151
152 _Future._catchError(this._onErrorCallback, this._errorTestCallback)
153 : _onValueCallback = null, _whenCompleteActionCallback = null {
154 _zone.expectCallback();
155 }
156
157 _Future._whenComplete(this._whenCompleteActionCallback)
158 : _onValueCallback = null, _errorTestCallback = null,
159 _onErrorCallback = null {
160 _zone.expectCallback();
161 }
162
163 Future then(f(T value), { onError(error) }) {
164 _Future result;
165 result = new _Future._then(f, onError);
166 _addListener(result);
167 return result;
168 }
169
170 Future catchError(f(error), { bool test(error) }) {
171 _Future result = new _Future._catchError(f, test);
172 _addListener(result);
173 return result;
174 }
175
176 Future<T> whenComplete(action()) {
177 _Future result = new _Future<T>._whenComplete(action);
178 _addListener(result);
179 return result;
180 }
181
182 Stream<T> asStream() => new Stream.fromFuture(this);
183
184 void _markPendingCompletion() {
185 if (!_mayComplete) throw new StateError("Future already completed");
186 _state = _PENDING_COMPLETE;
187 }
188
189 void _clearPendingCompletion() {
190 assert(_state == _PENDING_COMPLETE);
191 _state = _INCOMPLETE;
192 }
193
194 T get _value {
195 assert(_isComplete && _hasValue);
196 return _resultOrListeners;
197 }
198
199 Object get _error {
200 assert(_isComplete && _hasError);
201 return _resultOrListeners;
202 }
203
204 void _setValue(T value) {
205 assert(!_isComplete); // But may have a completion pending.
218 _state = _VALUE; 206 _state = _VALUE;
219 _resultOrListeners = value; 207 _resultOrListeners = value;
220 } 208 }
221 209
222 _FutureImpl.immediateError(var error, [Object stackTrace]) { 210 void _setError(Object error) {
223 if (stackTrace != null) { 211 assert(!_isComplete); // But may have a completion pending.
224 // Force stack trace onto error, even if it had already one. 212 _state = _ERROR;
225 _attachStackTrace(error, stackTrace); 213 _resultOrListeners = error;
226 }
227 _asyncSetError(error);
228 } 214 }
229 215
230 factory _FutureImpl.wait(Iterable<Future> futures) { 216 void _addListener(_Future listener) {
231 Completer completer;
232 // List collecting values from the futures.
233 // Set to null if an error occurs.
234 List values;
235 void handleError(error) {
236 if (values != null) {
237 values = null;
238 completer.completeError(error);
239 }
240 }
241 // As each future completes, put its value into the corresponding
242 // position in the list of values.
243 int remaining = 0;
244 for (Future future in futures) {
245 int pos = remaining++;
246 future.catchError(handleError).then((Object value) {
247 if (values == null) return null;
248 values[pos] = value;
249 remaining--;
250 if (remaining == 0) {
251 completer.complete(values);
252 }
253 });
254 }
255 if (remaining == 0) {
256 return new Future.value(const []);
257 }
258 values = new List(remaining);
259 completer = new Completer<List>();
260 return completer.future;
261 }
262
263 Future then(f(T value), { onError(error) }) {
264 if (onError == null) {
265 return new _ThenFuture(f).._subscribeTo(this);
266 }
267 return new _SubscribeFuture(f, onError).._subscribeTo(this);
268 }
269
270 Future catchError(f(error), { bool test(error) }) {
271 return new _CatchErrorFuture(f, test).._subscribeTo(this);
272 }
273
274 Future<T> whenComplete(action()) {
275 return new _WhenFuture<T>(action).._subscribeTo(this);
276 }
277
278 Stream<T> asStream() => new Stream.fromFuture(this);
279
280 bool _inSameErrorZone(_Zone otherZone) {
281 return _zone.inSameErrorZone(otherZone);
282 }
283
284 void _setValue(T value) {
285 if (!_mayComplete) throw new StateError("Future already completed");
286 _setValueUnchecked(value);
287 }
288
289 void _setValueUnchecked(T value) {
290 _FutureListener listeners = _isChained ? null : _removeListeners();
291 _state = _VALUE;
292 _resultOrListeners = value;
293 while (listeners != null) {
294 _FutureListener listener = listeners;
295 listeners = listener._nextListener;
296 listener._nextListener = null;
297 listener._sendValue(value);
298 }
299 }
300
301 void _setError(Object error) {
302 if (!_mayComplete) throw new StateError("Future already completed");
303 _setErrorUnchecked(error);
304 }
305
306 void _setErrorUnchecked(Object error) {
307 _FutureListener listeners;
308 bool hasListeners;
309 if (_isChained) {
310 listeners = null;
311 hasListeners = (_state == _CHAINED); // and not _CHAINED_UNLISTENED.
312 } else {
313 listeners = _removeListeners();
314 hasListeners = (listeners != null);
315 }
316
317 _state = _ERROR;
318 _resultOrListeners = error;
319
320 if (!hasListeners) {
321 // TODO(floitsch): Hook this into unhandled error handling.
322 var error = _resultOrListeners;
323 _zone.handleUncaughtError(error);
324 return;
325 }
326 while (listeners != null) {
327 _FutureListener listener = listeners;
328 listeners = listener._nextListener;
329 listener._nextListener = null;
330 listener._sendError(error);
331 }
332 }
333
334 void _asyncSetValue(T value) {
335 if (!_mayComplete) throw new StateError("Future already completed");
336 _state = _PENDING_COMPLETE;
337 runAsync(() { _setValueUnchecked(value); });
338 }
339
340 void _asyncSetError(Object error) {
341 if (!_mayComplete) throw new StateError("Future already completed");
342 _state = _PENDING_COMPLETE;
343 runAsync(() { _setErrorUnchecked(error); });
344 }
345
346 void _addListener(_FutureListener listener) {
347 assert(listener._nextListener == null); 217 assert(listener._nextListener == null);
348 if (!listener._inSameErrorZone(_zone)) {
349 listener = new _ErrorZoneBoundaryListener(listener);
350 }
351 if (_isChained) {
352 _state = _CHAINED; // In case it was _CHAINED_UNLISTENED.
353 _FutureImpl resultSource = _chainSource;
354 resultSource._addListener(listener);
355 return;
356 }
357 if (_isComplete) { 218 if (_isComplete) {
358 // Handle late listeners asynchronously. 219 // Handle late listeners asynchronously.
359 runAsync(() { 220 runAsync(() {
360 if (_hasValue) { 221 _propagateToSuccessors(this, listener);
361 T value = _resultOrListeners;
362 listener._sendValue(value);
363 } else {
364 assert(_hasError);
365 listener._sendError(_resultOrListeners);
366 }
367 }); 222 });
368 } else { 223 } else {
369 assert(!_isComplete);
370 listener._nextListener = _resultOrListeners; 224 listener._nextListener = _resultOrListeners;
371 _resultOrListeners = listener; 225 _resultOrListeners = listener;
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Ah, now I get it (where "now" is reading some code
floitsch 2013/09/10 17:19:14 too clever :)
372 } 226 }
373 } 227 }
374 228
375 _FutureListener _removeListeners() { 229 _Future _removeListeners() {
376 // Reverse listeners before returning them, so the resulting list is in 230 // Reverse listeners before returning them, so the resulting list is in
377 // subscription order. 231 // subscription order.
378 assert(!_isComplete); 232 assert(!_isComplete);
379 _FutureListener current = _resultOrListeners; 233 _Future current = _resultOrListeners;
380 _resultOrListeners = null; 234 _resultOrListeners = null;
381 _FutureListener prev = null; 235 _Future prev = null;
382 while (current != null) { 236 while (current != null) {
383 _FutureListener next = current._nextListener; 237 _Future next = current._nextListener;
384 current._nextListener = prev; 238 current._nextListener = prev;
385 prev = current; 239 prev = current;
386 current = next; 240 current = next;
387 } 241 }
388 return prev; 242 return prev;
389 } 243 }
390 244
245 static void _chainFutures(Future source, _Future target) {
246 assert(!target._isComplete);
247
248 // Mark the target as chained (and as such half-completed).
249 target._isChained = true;
250 if (source is _Future) {
251 _Future internalFuture = source;
252 if (internalFuture._isComplete) {
253 _propagateToSuccessors(internalFuture, target);
254 } else {
255 internalFuture._addListener(target);
256 }
257 } else {
258 source.then((value) {
259 // Clear the is-chained bit, so that we can use the standard
260 // _complete method.
261 target._isChained = false;
262 target._complete(value);
263 },
264 onError: (error) {
265 // Clear the is-chained bit, so that we can use the standard
266 // _completeError method.
267 target._isChained = false;
268 target._completeError(error);
269 });
270 }
271 }
272
273 void _complete(value) {
274 assert(_onValueCallback == null &&
275 _onErrorCallback == null &&
276 _whenCompleteActionCallback == null &&
277 _errorTestCallback == null);
278 if (!_mayComplete) throw new StateError("Future already completed");
279 if (value is Future) {
280 _chainFutures(value, this);
281 return;
282 }
283 _Future listeners = _removeListeners();
284 _setValue(value);
285 _propagateToSuccessors(this, listeners);
286 }
287
288 void _completeError(error, [StackTrace stackTrace]) {
289 assert(_onValueCallback == null &&
290 _onErrorCallback == null &&
291 _whenCompleteActionCallback == null &&
292 _errorTestCallback == null);
293 // _isComplete does not trigger for pending completions.
294 if (!_mayComplete) throw new StateError("Future already completed");
295 if (stackTrace != null) {
296 // Force the stack trace onto the error, even if it already had one.
297 _attachStackTrace(error, stackTrace);
298 }
299
300 _Future listeners = _isChained ? null : _removeListeners();
301 _setError(error);
302 _propagateToSuccessors(this, listeners);
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Does it make sense to call this if listeners is nu
floitsch 2013/09/10 17:19:14 Yes. Because that's where we need to trigger an un
303 }
304
305 void _asyncComplete(value) {
306 assert(_onValueCallback == null &&
307 _onErrorCallback == null &&
308 _whenCompleteActionCallback == null &&
309 _errorTestCallback == null);
310 if (!_mayComplete) throw new StateError("Future already completed");
311 // Two corner cases if the value is a future:
312 // 1. the future is already completed and an error.
313 // 2. the future is not yet completed but might become an error.
314 // The first case means that we must not immediately complete the Future,
315 // as our code would immediately start propagating the error without
316 // giving the time to install error-handlers.
317 // However the second case requires us to deal with the value immediately.
318 // Otherwise the value could complete with an error and report an
319 // unhandled error, even though we know we are already going to listen to
320 // it.
321 if (value is Future &&
322 (value is! _Future || !(value as _Future)._isComplete)) {
323 // Case 2 from above. We need to register.
324 // Note that we are still completing asynchronously: either we register
325 // through .then (in which case the completing is asynchronous), or we
326 // have a _Future which isn't complete yet.
327 _complete(value);
328 return;
329 }
330
331 _markPendingCompletion();
332 runAsync(() {
333 _clearPendingCompletion();
334 _complete(value);
335 });
336 }
337
338 _asyncCompleteError(error, [StackTrace stackTrace]) {
kevmoo-old 2013/09/09 19:27:45 void?
floitsch 2013/09/10 17:19:14 Done.
339 assert(_onValueCallback == null &&
340 _onErrorCallback == null &&
Lasse Reichstein Nielsen 2013/09/10 12:03:53 indent to after '('. Or, preferably, make this fou
floitsch 2013/09/10 17:19:14 Done.
341 _whenCompleteActionCallback == null &&
342 _errorTestCallback == null);
343 if (!_mayComplete) throw new StateError("Future already completed");
344 _markPendingCompletion();
345 runAsync(() {
346 _clearPendingCompletion();
347 _completeError(error, stackTrace);
348 });
349 }
350
391 /** 351 /**
392 * Make another [_FutureImpl] receive the result of this one. 352 * Propagates the value/error of [source] to its [listeners], executing the
393 * 353 * listeners' callbacks.
394 * If this future is already complete, the [future] is notified 354 *
395 * immediately. This function is only called during event resolution 355 * If [runCallback] is true (which should be the default) it executes
396 * where it's acceptable to send an event. 356 * the registered action of listeners. If it is `false` then the callback is
357 * skipped. This is used to complete futures with chained futures.
397 */ 358 */
398 void _chain(_FutureImpl future) { 359 static void _propagateToSuccessors(_Future source, _Future listeners) {
399 if (!_isComplete) { 360 while (true) {
400 future._chainFromFuture(this); 361 if (!source._isComplete) return; // Chained future.
401 } else if (_hasValue) { 362 bool hasError = source._hasError;
402 future._setValue(_resultOrListeners); 363 if (hasError && listeners == null) {
403 } else { 364 source._zone.handleUncaughtError(source._error);
404 assert(_hasError); 365 return;
405 future._setError(_resultOrListeners); 366 }
406 } 367 if (listeners == null) return;
407 } 368 _Future listener;
408 369 _Future remainingListeners = listeners;
409 /** 370 do {
410 * Returns the future that this future is chained to. 371 // We handle each listener individually, using the stack as a queue.
411 * 372 // Usually there is only one listener, so this should not be a problem.
412 * If that future is itself chained to something else, 373 listener = remainingListeners; // Just an alias.
413 * get the [_chainSource] of that future instead, and make this 374 remainingListeners = remainingListeners._nextListener;
414 * future chain directly to the earliest source. 375 // Cut the connection to the remaining listeners. This way we can
415 */ 376 // call _propagateToSuccessors without fearing that it may have an
416 _FutureImpl get _chainSource { 377 // impact on other listeners.
417 assert(_isChained); 378 listener._nextListener = null;
418 _FutureImpl future = _resultOrListeners; 379 if (remainingListeners != null) {
419 if (future._isChained) { 380 _propagateToSuccessors(source, listener);
Lasse Reichstein Nielsen 2013/09/10 12:03:53 This seems like an awkward way to do a loop. Drop
floitsch 2013/09/11 09:29:30 created helper function.
420 future = _resultOrListeners = future._chainSource; 381 }
421 } 382 } while (remainingListeners != null);
422 return future; 383
423 } 384 if (hasError && !source._zone.inSameErrorZone(listener._zone)) {
424 385 // Don't cross zone boundaries with errors.
425 /** 386 source._zone.handleUncaughtError(source._error);
426 * Make this incomplete future end up with the same result as [resultSource]. 387 return;
427 * 388 }
428 * This is done by moving all listeners to [resultSource] and forwarding all 389 if (!identical(_Zone.current, listener._zone)) {
429 * future [_addListener] calls to [resultSource] directly. 390 // Run the propagation in the listener's zone to avoid
430 */ 391 // zone transitions. The idea is that many chained futures will
431 void _chainFromFuture(_FutureImpl resultSource) { 392 // be in the same zone.
432 assert(!_isComplete); 393 listener._zone.executePeriodicCallback(() {
433 assert(!_isChained); 394 _propagateToSuccessors(source, listeners);
Lasse Reichstein Nielsen 2013/09/10 12:03:53 listeners -> listener (same thing, easier to read
floitsch 2013/09/10 17:19:14 Done.
434 if (resultSource._isChained) { 395 });
435 resultSource = resultSource._chainSource; 396 return;
436 } 397 }
437 assert(!resultSource._isChained); 398 // Normal listener.
Lasse Reichstein Nielsen 2013/09/10 12:03:53 What does "normal" mean? Seems to be "in the same
floitsch 2013/09/10 17:19:14 Replaced with "// Do actual propagation".
438 if (identical(this, resultSource)) { 399 // TODO(floitsch): Do we need to go through the zone even if we
439 // The only unchained future in a future dependency tree (as defined 400 // don't have a callback to execute?
440 // by the chain-relations) is the "root" that every other future depends 401 bool listenerHasValue;
441 // on. The future we are adding is unchained, so if it is already in the 402 var listenerValueOrError;
442 // tree, it must be the root, so that's the only one we need to check 403 // Set to true if a whenComplete needs to wait for a future.
443 // against to detect a cycle. 404 // The whenComplete action will resume the propagation by itself.
444 _setError(new StateError("Cyclic future dependency.")); 405 bool isPropagationAborted = false;
445 return; 406 // Even though we are already in the right zone (due to the optimization
446 } 407 // above), we still need to go through the zone. The overhead of
447 _FutureListener cursor = _removeListeners(); 408 // executeCallback is however smaller when it is already in the correct
448 bool hadListeners = cursor != null; 409 // zone.
449 while (cursor != null) { 410 // TODO(floitsch): only run callbacks in the zone, not the whole
450 _FutureListener listener = cursor; 411 // handling code.
451 cursor = cursor._nextListener; 412 listener._zone.executeCallback(() {
452 listener._nextListener = null; 413 // TODO(floitsch): mark the listener as pending completion. Currently
453 resultSource._addListener(listener); 414 // we can't do this, since the markPendingCompletion verifies that
454 } 415 // the future is not already marked (or chained).
455 // Listen with this future as well, so that when the other future completes, 416 try {
456 // this future will be completed as well. 417 if (!hasError) {
457 resultSource._addListener(this._asListener()); 418 var value = source._value;
458 _resultOrListeners = resultSource; 419 if (listener._onValue != null) {
459 _state = hadListeners ? _CHAINED : _CHAINED_UNLISTENED; 420 listenerValueOrError = listener._onValue(value);
460 } 421 listenerHasValue = true;
461 422 } else {
462 /** 423 // Copy over the value from the source.
463 * Helper function to handle the result of transforming an incoming event. 424 listenerValueOrError = value;
464 * 425 listenerHasValue = true;
465 * If the result is itself a [Future], this future is linked to that 426 }
466 * future's output. If not, this future is completed with the result. 427 } else {
467 */ 428 Object error = source._error;
468 void _setOrChainValue(var result) { 429 _FutureErrorTest test = listener._errorTest;
469 assert(!_isChained); 430 bool matchesTest = true;
470 assert(!_isComplete); 431 if (test != null) {
471 if (result is Future) { 432 matchesTest = test(error);
472 // Result should be a Future<T>. 433 }
473 if (result is _FutureImpl) { 434 if (matchesTest && listener._onError != null) {
474 _FutureImpl chainFuture = result; 435 listenerValueOrError = listener._onError(error);
475 chainFuture._chain(this); 436 listenerHasValue = true;
476 return; 437 } else {
438 // Copy over the error from the source.
439 listenerValueOrError = error;
440 listenerHasValue = false;
441 }
442 }
443
444 if (listener._whenCompleteAction != null) {
445 var completeResult = listener._whenCompleteAction();
446 if (completeResult is Future) {
447 listener._isChained = true;
448 completeResult.then((ignored) {
449 // Try again, but this time don't run the whenComplete callback.
450 _propagateToSuccessors(source, listener);
451 }, onError: (error) {
452 // When there is an error, we have to make the error the new
453 // result of the current listener.
454 if (completeResult is! _Future) {
455 // This should be a rare case.
456 completeResult = new _Future();
Lasse Reichstein Nielsen 2013/09/10 12:03:53 Is there an .immediateError constructor?
floitsch 2013/09/10 17:19:14 There is, but even though it is called "immediateE
457 completeResult._setError(error);
458 }
459 _propagateToSuccessors(completeResult, listener);
460 });
461 isPropagationAborted = true;
462 // We will reenter the listener's zone.
463 listener._zone.expectCallback();
464 }
465 }
466 } catch (e, s) {
467 // Set the exception as error.
468 listenerValueOrError = _asyncError(e, s);
469 listenerHasValue = false;
470 }
471 if (listenerHasValue && listenerValueOrError is Future) {
472 // We are going to reenter the zone to finish what we started.
473 listener._zone.expectCallback();
474 }
475 });
476 if (isPropagationAborted) return;
477 // If the listener's value is a future we need to chain it.
478 if (listenerHasValue && listenerValueOrError is Future) {
479 Future chainSource = listenerValueOrError;
480 // Shortcut if the chain-source is already completed. Just continue the
481 // loop.
482 if (chainSource is _Future && (chainSource as _Future)._isComplete) {
483 // propagate the value (simulating a tail call).
484 listener._isChained = true;
485 source = chainSource;
486 listeners = listener;
487 continue;
488 }
489 _chainFutures(chainSource, listener);
490 return;
491 }
492
493 if (listenerHasValue) {
494 listeners = listener._removeListeners();
495 listener._setValue(listenerValueOrError);
477 } else { 496 } else {
478 Future future = result; 497 listeners = listener._removeListeners();
479 future.then(_setValue, 498 listener._setError(listenerValueOrError);
480 onError: _setError); 499 }
481 return; 500 // Prepare for next round.
482 } 501 source = listener;
483 } else { 502 }
484 // Result must be of type T. 503 }
485 _setValue(result);
486 }
487 }
488
489 _FutureListener _asListener() => new _FutureListener.wrap(this);
490 } 504 }
491
492 /**
493 * Transforming future base class.
494 *
495 * A transforming future is itself a future and a future listener.
496 * Subclasses override [_sendValue]/[_sendError] to intercept
497 * the results of a previous future.
498 */
499 abstract class _TransformFuture<S, T> extends _FutureImpl<T>
500 implements _FutureListener<S> {
501 // _FutureListener implementation.
502 _FutureListener _nextListener;
503
504 _TransformFuture() {
505 _zone.expectCallback();
506 }
507
508 void _sendValue(S value) {
509 _zone.executeCallback(() => _zonedSendValue(value));
510 }
511
512 void _sendError(error) {
513 _zone.executeCallback(() => _zonedSendError(error));
514 }
515
516 void _subscribeTo(_FutureImpl future) {
517 future._addListener(this);
518 }
519
520 void _zonedSendValue(S value);
521 void _zonedSendError(error);
522 }
523
524 /** The onValue and onError handlers return either a value or a future */
525 typedef dynamic _FutureOnValue<T>(T value);
526 typedef dynamic _FutureOnError(error);
527 /** Test used by [Future.catchError] to handle skip some errors. */
528 typedef bool _FutureErrorTest(var error);
529 /** Used by [WhenFuture]. */
530 typedef _FutureAction();
531
532 /** Future returned by [Future.then] with no [:onError:] parameter. */
533 class _ThenFuture<S, T> extends _TransformFuture<S, T> {
534 // TODO(ahe): Restore type when feature is implemented in dart2js
535 // checked mode.
536 final /* _FutureOnValue<S> */ _onValue;
537
538 _ThenFuture(this._onValue);
539
540 _zonedSendValue(S value) {
541 assert(_onValue != null);
542 var result;
543 try {
544 result = _onValue(value);
545 } catch (e, s) {
546 _setError(_asyncError(e, s));
547 return;
548 }
549 _setOrChainValue(result);
550 }
551
552 void _zonedSendError(error) {
553 _setError(error);
554 }
555 }
556
557 /** Future returned by [Future.catchError]. */
558 class _CatchErrorFuture<T> extends _TransformFuture<T,T> {
559 final _FutureErrorTest _test;
560 final _FutureOnError _onError;
561
562 _CatchErrorFuture(this._onError, this._test);
563
564 _zonedSendValue(T value) {
565 _setValue(value);
566 }
567
568 _zonedSendError(error) {
569 assert(_onError != null);
570 // if _test is supplied, check if it returns true, otherwise just
571 // forward the error unmodified.
572 if (_test != null) {
573 bool matchesTest;
574 try {
575 matchesTest = _test(error);
576 } catch (e, s) {
577 _setError(_asyncError(e, s));
578 return;
579 }
580 if (!matchesTest) {
581 _setError(error);
582 return;
583 }
584 }
585 // Act on the error, and use the result as this future's result.
586 var result;
587 try {
588 result = _onError(error);
589 } catch (e, s) {
590 _setError(_asyncError(e, s));
591 return;
592 }
593 _setOrChainValue(result);
594 }
595 }
596
597 /** Future returned by [Future.then] with an [:onError:] parameter. */
598 class _SubscribeFuture<S, T> extends _ThenFuture<S, T> {
599 final _FutureOnError _onError;
600
601 _SubscribeFuture(onValue(S value), this._onError) : super(onValue);
602
603 // The _sendValue method is inherited from ThenFuture.
604
605 void _zonedSendError(error) {
606 assert(_onError != null);
607 var result;
608 try {
609 result = _onError(error);
610 } catch (e, s) {
611 _setError(_asyncError(e, s));
612 return;
613 }
614 _setOrChainValue(result);
615 }
616 }
617
618 /** Future returned by [Future.whenComplete]. */
619 class _WhenFuture<T> extends _TransformFuture<T, T> {
620 final _FutureAction _action;
621
622 _WhenFuture(this._action);
623
624 void _zonedSendValue(T value) {
625 try {
626 var result = _action();
627 if (result is Future) {
628 Future resultFuture = result;
629 resultFuture.then((_) {
630 _setValue(value);
631 }, onError: _setError);
632 return;
633 }
634 } catch (e, s) {
635 _setError(_asyncError(e, s));
636 return;
637 }
638 _setValue(value);
639 }
640
641 void _zonedSendError(error) {
642 try {
643 var result = _action();
644 if (result is Future) {
645 Future resultFuture = result;
646 // TODO(lrn): Find a way to combine [error] into [e].
647 resultFuture.then((_) {
648 _setError(error);
649 }, onError: _setError);
650 return;
651 }
652 } catch (e, s) {
653 error = _asyncError(e, s);
654 }
655 _setError(error);
656 }
657 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698