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

Unified Diff: sdk/lib/async/future_impl.dart

Issue 1510053004: Simplify future-propagation for whenComplete. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Address comments Created 5 years 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/async/future_impl.dart
diff --git a/sdk/lib/async/future_impl.dart b/sdk/lib/async/future_impl.dart
index 35dfd1194f052403843fff9fef9fef1ebcaf706e..f27b34dce6c77990bee147b542f7e172ba205870 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -187,20 +187,16 @@ class _Future<T> implements Future<T> {
_asyncCompleteError(error, stackTrace);
}
- bool get _mayComplete => _state == _INCOMPLETE;
- bool get _isChained => _state == _CHAINED;
- bool get _isComplete => _state >= _VALUE;
- bool get _hasValue => _state == _VALUE;
- bool get _hasError => _state == _ERROR;
+ bool get _mayComplete => _state == _INCOMPLETE;
+ bool get _isPendingComplete => _state == _PENDING_COMPLETE;
+ bool get _isChained => _state == _CHAINED;
+ bool get _isComplete => _state >= _VALUE;
+ bool get _hasValue => _state == _VALUE;
+ bool get _hasError => _state == _ERROR;
- set _isChained(bool value) {
- if (value) {
- assert(!_isComplete);
- _state = _CHAINED;
- } else {
- assert(_isChained);
- _state = _INCOMPLETE;
- }
+ void _setChained() {
+ assert(!_isComplete);
+ _state = _CHAINED;
}
Future then(f(T value), { Function onError }) {
@@ -304,14 +300,15 @@ class _Future<T> implements Future<T> {
}
// Take the value (when completed) of source and complete target with that
- // value (or error). This function can chain all Futures, but is slower
- // for _Future than _chainCoreFuture - Use _chainCoreFuture in that case.
+ // value (or error). This function could chain all Futures, but is slower
+ // for _Future than _chainCoreFuture, so you must use _chainCoreFuture
+ // in that case.
static void _chainForeignFuture(Future source, _Future target) {
assert(!target._isComplete);
assert(source is! _Future);
// Mark the target as chained (and as such half-completed).
- target._isChained = true;
+ target._setChained();
try {
source.then((value) {
assert(target._isChained);
@@ -343,7 +340,7 @@ class _Future<T> implements Future<T> {
assert(source is _Future);
// Mark the target as chained (and as such half-completed).
- target._isChained = true;
+ target._setChained();
_FutureListener listener = new _FutureListener.chain(target);
if (source._isComplete) {
_propagateToListeners(source, listener);
@@ -404,7 +401,7 @@ class _Future<T> implements Future<T> {
Future<T> typedFuture = value;
if (typedFuture is _Future) {
_Future<T> coreFuture = typedFuture;
- if (coreFuture._isComplete && coreFuture._hasError) {
+ if (coreFuture._hasError) {
// Case 1 from above. Delay completion to enable the user to register
// callbacks.
_markPendingCompletion();
@@ -467,21 +464,16 @@ class _Future<T> implements Future<T> {
}
_FutureListener listener = listeners;
// Do the actual propagation.
- // Set initial state of listenerHasValue and listenerValueOrError. These
- // variables are updated, with the outcome of potential callbacks.
- bool listenerHasValue = true;
- final sourceValue = hasError ? null : source._value;
- var listenerValueOrError = sourceValue;
- // Set to true if a whenComplete needs to wait for a future.
- // The whenComplete action will resume the propagation by itself.
- bool isPropagationAborted = false;
- // TODO(floitsch): mark the listener as pending completion. Currently
- // we can't do this, since the markPendingCompletion verifies that
- // the future is not already marked (or chained).
+ // Set initial state of listenerHasError and listenerValueOrError. These
+ // variables are updated with the outcome of potential callbacks.
+ bool listenerHasError = hasError;
+ final sourceResult = source._resultOrListeners;
+ var listenerValueOrError = sourceResult;
+
// Only if we either have an error or callbacks, go into this, somewhat
// expensive, branch. Here we'll enter/leave the zone. Many futures
- // doesn't have callbacks, so this is a significant optimization.
- if (hasError || (listener.handlesValue || listener.handlesComplete)) {
+ // don't have callbacks, so this is a significant optimization.
+ if (hasError || listener.handlesValue || listener.handlesComplete) {
Zone zone = listener._zone;
if (hasError && !source._zone.inSameErrorZone(zone)) {
// Don't cross zone boundaries with errors.
@@ -497,14 +489,15 @@ class _Future<T> implements Future<T> {
oldZone = Zone._enter(zone);
}
- bool handleValueCallback() {
+ void handleValueCallback() {
+ assert(!hasError);
try {
listenerValueOrError = zone.runUnary(listener._onValue,
- sourceValue);
- return true;
+ sourceResult);
+ listenerHasError = false;
} catch (e, s) {
listenerValueOrError = new AsyncError(e, s);
- return false;
+ listenerHasError = true;
}
}
@@ -516,9 +509,10 @@ class _Future<T> implements Future<T> {
try {
matchesTest = zone.runUnary(test, asyncError.error);
} catch (e, s) {
- listenerValueOrError = identical(asyncError.error, e) ?
- asyncError : new AsyncError(e, s);
- listenerHasValue = false;
+ listenerValueOrError = identical(asyncError.error, e)
+ ? asyncError
+ : new AsyncError(e, s);
+ listenerHasError = true;
return;
}
}
@@ -533,21 +527,18 @@ class _Future<T> implements Future<T> {
listenerValueOrError = zone.runUnary(errorCallback,
asyncError.error);
}
+ listenerHasError = false;
} catch (e, s) {
- listenerValueOrError = identical(asyncError.error, e) ?
- asyncError : new AsyncError(e, s);
- listenerHasValue = false;
- return;
+ listenerValueOrError = identical(asyncError.error, e)
+ ? asyncError
+ : new AsyncError(e, s);
+ listenerHasError = true;
}
- listenerHasValue = true;
- } else {
- // Copy over the error from the source.
- listenerValueOrError = asyncError;
- listenerHasValue = false;
}
}
void handleWhenCompleteCallback() {
+ assert(!listener.handlesError);
var completeResult;
try {
completeResult = zone.run(listener._whenCompleteAction);
@@ -557,49 +548,50 @@ class _Future<T> implements Future<T> {
} else {
listenerValueOrError = new AsyncError(e, s);
}
- listenerHasValue = false;
+ listenerHasError = true;
return;
}
if (completeResult is Future) {
- _Future result = listener.result;
- result._isChained = true;
- isPropagationAborted = true;
- completeResult.then((ignored) {
- _propagateToListeners(source, new _FutureListener.chain(result));
- }, onError: (error, [stackTrace]) {
- // When there is an error, we have to make the error the new
- // result of the current listener.
- if (completeResult is! _Future) {
- // This should be a rare case.
- completeResult = new _Future();
- completeResult._setError(error, stackTrace);
+ if (completeResult is _Future && completeResult._isComplete) {
+ if (completeResult._hasError) {
+ listenerValueOrError = completeResult._error;
+ listenerHasError = true;
}
- _propagateToListeners(completeResult,
- new _FutureListener.chain(result));
- });
+ // Otherwise use the existing result of source.
+ return;
+ }
+ // We have to wait for the completeResult future to complete
+ // before knowing if it's an error or we should use the result
+ // of source.
+ var originalSource = source;
+ listenerValueOrError = completeResult.then((_) => originalSource);
+ listenerHasError = false;
}
}
- if (!hasError) {
- if (listener.handlesValue) {
- listenerHasValue = handleValueCallback();
- }
- } else {
- handleError();
- }
if (listener.handlesComplete) {
+ // The whenComplete-handler is not combined with normal value/error
+ // handling. This means at most one handleX method is called per
+ // listener.
+ assert(!listener.handlesValue);
+ assert(!listener.handlesError);
handleWhenCompleteCallback();
+ } else if (!hasError) {
+ if (listener.handlesValue) {
+ handleValueCallback();
+ }
+ } else {
+ if (listener.handlesError) {
+ handleError();
+ }
}
+
// If we changed zone, oldZone will not be null.
if (oldZone != null) Zone._leave(oldZone);
- if (isPropagationAborted) return;
// If the listener's value is a future we need to chain it. Note that
- // this can only happen if there is a callback. Since 'is' checks
- // can be expensive, we're trying to avoid it.
- if (listenerHasValue &&
- !identical(sourceValue, listenerValueOrError) &&
- listenerValueOrError is Future) {
+ // this can only happen if there is a callback.
+ if (listenerValueOrError is Future) {
Future chainSource = listenerValueOrError;
// Shortcut if the chain-source is already completed. Just continue
// the loop.
@@ -607,7 +599,7 @@ class _Future<T> implements Future<T> {
if (chainSource is _Future) {
if (chainSource._isComplete) {
// propagate the value (simulating a tail call).
- result._isChained = true;
+ result._setChained();
source = chainSource;
listeners = new _FutureListener.chain(result);
continue;
@@ -622,7 +614,7 @@ class _Future<T> implements Future<T> {
}
_Future result = listener.result;
listeners = result._removeListeners();
- if (listenerHasValue) {
+ if (!listenerHasError) {
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698