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

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

Issue 702413002: Revert "Convert _propagateToListeners to using an external stack instead of recursion." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 | sdk/lib/async/zone.dart » ('j') | 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 a7e5cb888226325ac880b01a9a03ac51899f9834..a0cfeb16efe9b05e4778085a0988824e46168236 100644
--- a/sdk/lib/async/future_impl.dart
+++ b/sdk/lib/async/future_impl.dart
@@ -11,9 +11,6 @@ typedef bool _FutureErrorTest(var error);
/** Used by [WhenFuture]. */
typedef _FutureAction();
-const bool _GUARDED = true;
-const bool _UNGUARDED = false;
-
abstract class _Completer<T> implements Completer<T> {
final _Future<T> future = new _Future<T>();
@@ -274,7 +271,7 @@ class _Future<T> implements Future<T> {
if (_isComplete) {
// Handle late listeners asynchronously.
_zone.scheduleMicrotask(() {
- _propagateToFutures(this, listener, _UNGUARDED);
+ _propagateToListeners(this, listener);
});
} else {
listener._nextListener = _resultOrListeners;
@@ -331,7 +328,7 @@ class _Future<T> implements Future<T> {
target._isChained = true;
_FutureListener listener = new _FutureListener.chain(target);
if (source._isComplete) {
- _propagateToFutures(source, listener, _GUARDED);
+ _propagateToListeners(source, listener);
} else {
source._addListener(listener);
}
@@ -348,7 +345,7 @@ class _Future<T> implements Future<T> {
} else {
_FutureListener listeners = _removeListeners();
_setValue(value);
- _propagateToFutures(this, listeners, _GUARDED);
+ _propagateToListeners(this, listeners);
}
}
@@ -358,7 +355,7 @@ class _Future<T> implements Future<T> {
_FutureListener listeners = _removeListeners();
_setValue(value);
- _propagateToFutures(this, listeners, _GUARDED);
+ _propagateToListeners(this, listeners);
}
void _completeError(error, [StackTrace stackTrace]) {
@@ -366,7 +363,7 @@ class _Future<T> implements Future<T> {
_FutureListener listeners = _removeListeners();
_setError(error, stackTrace);
- _propagateToFutures(this, listeners, _GUARDED);
+ _propagateToListeners(this, listeners);
}
void _asyncComplete(value) {
@@ -426,109 +423,36 @@ class _Future<T> implements Future<T> {
}
/**
- * Stack implemented by linked list of [_PendingFutureListeners].
- *
- * Each `_PendingFutureListeners` have on source future and one or more
- * listeners on that future (linked internally through
- * [_FutureListener._nextListener]).
- *
- * While propagating future results to listeners, pending listeners are
- * stored on this stack.
- */
- static _PendingFutureListeners _pendingFutureListeners = null;
-
- /**
- * Handle an uncaught async error.
- *
- * If the [guarded] flag is `true`, or the future's zone has an error
- * handler, the uncaught error is passed to the zone's error handler.
- * Otherwise, the uncaught error is thrown immediately, and is expected
- * to propagate to the event loop. (If it can't propagate to the event
- * loop, `guarded` should be true).
- */
- static void _throwUncaughtError(_Future source, bool guarded) {
- assert(source._hasError);
- AsyncError asyncError = source._error;
- Zone zone = source._zone;
- if (guarded || !identical(zone.errorZone, _ROOT_ZONE)) {
- zone.handleUncaughtError(asyncError.error, asyncError.stackTrace);
- if (_pendingFutureListeners != null) {
- _schedulePriorityAsyncCallback(_propagateToFutures);
- }
- return;
- }
- if (_pendingFutureListeners != null) {
- _schedulePriorityAsyncCallback(_propagateToFutures);
- }
- throw new _UncaughtAsyncError(asyncError.error, asyncError.stackTrace);
- }
-
- /**
* Propagates the value/error of [source] to its [listeners], executing the
* listeners' callbacks.
- *
- * The [guarded] flag should be set when the function isn't being called in
- * tail position of an event, or if simply throwing an uncaught error is
- * otherwise not acceptable.
- *
- * Parameters are optional to allow using [_propagateToFutures] directly
- * as a scheduled function. In that case, it takes its sources from
- * [_pendingFutureListeners].
*/
- static void _propagateToFutures([_Future source,
- _FutureListener listeners,
- bool guarded = _UNGUARDED]) {
+ static void _propagateToListeners(_Future source, _FutureListener listeners) {
while (true) {
- if (source == null) {
- // Pop source and listeners from _pendingFutureListeners.
- // This always sets `listeners` to be a single listener.
- if (_pendingFutureListeners == null) return;
- _PendingFutureListeners pending = _pendingFutureListeners;
- // Read top-most source and listener.
- source = pending.source;
- listeners = pending.listeners;
- // Remove the top-most listener. If it's the last listener for
- // source, go to the next _PendingFutureListeners block.
- if (listeners != null && listeners._nextListener != null) {
- pending.listeners = listeners._nextListener;
- listeners._nextListener = null;
- } else {
- _pendingFutureListeners = pending.next;
- }
- } else {
- // Check if there are zero listeners, or more than one listener.
- assert(source._isComplete);
- if (listeners == null) {
- // If zero listeners, and source has an error, consider the error
- // uncaught.
- if (source._hasError) {
- _throwUncaughtError(source, guarded);
- return;
- }
- if (_pendingFutureListeners == null) return;
- source = null;
- continue;
- } else if (listeners._nextListener != null) {
- // Usually futures only have one listener.
- // If they have several, we put the rest on the pending listener
- // stack and handle them later.
- _pendingFutureListeners =
- new _PendingFutureListeners(source, listeners._nextListener,
- _pendingFutureListeners);
- listeners._nextListener = null;
+ assert(source._isComplete);
+ bool hasError = source._hasError;
+ if (listeners == null) {
+ if (hasError) {
+ AsyncError asyncError = source._error;
+ source._zone.handleUncaughtError(
+ asyncError.error, asyncError.stackTrace);
}
+ return;
+ }
+ // Usually futures only have one listener. If they have several, we
+ // call handle them separately in recursive calls, continuing
+ // here only when there is only one listener left.
+ while (listeners._nextListener != null) {
+ _FutureListener listener = listeners;
+ listeners = listener._nextListener;
+ listener._nextListener = null;
+ _propagateToListeners(source, listener);
}
- assert(source != null);
- assert(listeners != null);
- assert(listeners._nextListener == null);
_FutureListener listener = listeners;
-
- bool hasError = source._hasError;
// Do the actual propagation.
- // Set initial state of listenerHasError and listenerValueOrError. These
+ // Set initial state of listenerHasValue and listenerValueOrError. These
// variables are updated, with the outcome of potential callbacks.
- bool listenerHasError = hasError;
- final sourceValue = hasError ? source._error : source._value;
+ 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.
@@ -541,9 +465,32 @@ class _Future<T> implements Future<T> {
// doesn'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.
+ AsyncError asyncError = source._error;
+ source._zone.handleUncaughtError(
+ asyncError.error, asyncError.stackTrace);
+ return;
+ }
+
+ Zone oldZone;
+ if (!identical(Zone.current, zone)) {
+ // Change zone if it's not current.
+ oldZone = Zone._enter(zone);
+ }
+
+ bool handleValueCallback() {
+ try {
+ listenerValueOrError = zone.runUnary(listener._onValue,
+ sourceValue);
+ return true;
+ } catch (e, s) {
+ listenerValueOrError = new AsyncError(e, s);
+ return false;
+ }
+ }
void handleError() {
- assert(listener.handlesError);
AsyncError asyncError = source._error;
bool matchesTest = true;
if (listener.hasErrorTest) {
@@ -553,13 +500,12 @@ class _Future<T> implements Future<T> {
} catch (e, s) {
listenerValueOrError = identical(asyncError.error, e) ?
asyncError : new AsyncError(e, s);
- listenerHasError = true;
+ listenerHasValue = false;
return;
}
}
- if (matchesTest) {
- Function errorCallback = listener._onError;
- assert(errorCallback != null);
+ Function errorCallback = listener._onError;
+ if (matchesTest && errorCallback != null) {
try {
if (errorCallback is ZoneBinaryCallback) {
listenerValueOrError = zone.runBinary(errorCallback,
@@ -572,10 +518,14 @@ class _Future<T> implements Future<T> {
} catch (e, s) {
listenerValueOrError = identical(asyncError.error, e) ?
asyncError : new AsyncError(e, s);
- listenerHasError = true;
+ listenerHasValue = false;
return;
}
- listenerHasError = false;
+ listenerHasValue = true;
+ } else {
+ // Copy over the error from the source.
+ listenerValueOrError = asyncError;
+ listenerHasValue = false;
}
}
@@ -589,18 +539,15 @@ class _Future<T> implements Future<T> {
} else {
listenerValueOrError = new AsyncError(e, s);
}
- listenerHasError = true;
+ listenerHasValue = false;
return;
}
if (completeResult is Future) {
_Future result = listener.result;
result._isChained = true;
isPropagationAborted = true;
- final _Future originalSource = source;
completeResult.then((ignored) {
- _propagateToFutures(originalSource,
- new _FutureListener.chain(result),
- _UNGUARDED);
+ _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.
@@ -609,63 +556,30 @@ class _Future<T> implements Future<T> {
completeResult = new _Future();
completeResult._setError(error, stackTrace);
}
- _propagateToFutures(completeResult,
- new _FutureListener.chain(result),
- _UNGUARDED);
+ _propagateToListeners(completeResult,
+ new _FutureListener.chain(result));
});
}
}
- Zone oldZone = Zone._enter(zone);
-
if (!hasError) {
if (listener.handlesValue) {
- // Run try/catch in separate function to help compilers.
- () {
- try {
- listenerValueOrError = zone.runUnary(listener._onValue,
- sourceValue);
- listenerHasError = false;
- } catch (e, s) {
- listenerValueOrError = new AsyncError(e, s);
- listenerHasError = true;
- }
- }();
- assert(!listener.handlesComplete);
- } else if (listener.handlesComplete) {
- // Complete-listeners are distinct from value/error handlers,
- // so no need to check for a complete handler if it is an error
- // handler.
- handleWhenCompleteCallback();
+ listenerHasValue = handleValueCallback();
}
} else {
- if (!source._zone.inSameErrorZone(zone)) {
- // Don't cross zone boundaries with errors.
- Zone._leave(oldZone);
- _throwUncaughtError(source, guarded);
- return;
- }
- if (listener.handlesError) {
- handleError();
- assert(!listener.handlesComplete);
- } else if (listener.handlesComplete) {
- handleWhenCompleteCallback();
- }
+ handleError();
}
-
- Zone._leave(oldZone);
-
- if (isPropagationAborted) {
- // Don't use the value in listenerValueOrError - the whenComplete
- // handler is handling that result.
- if (_pendingFutureListeners == null) return;
- source = null;
- continue;
+ if (listener.handlesComplete) {
+ handleWhenCompleteCallback();
}
+ // 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 (!listenerHasError &&
+ if (listenerHasValue &&
!identical(sourceValue, listenerValueOrError) &&
listenerValueOrError is Future) {
Future chainSource = listenerValueOrError;
@@ -674,7 +588,7 @@ class _Future<T> implements Future<T> {
_Future result = listener.result;
if (chainSource is _Future) {
if (chainSource._isComplete) {
- // Propagate the value (simulating a tail call).
+ // propagate the value (simulating a tail call).
result._isChained = true;
source = chainSource;
listeners = new _FutureListener.chain(result);
@@ -685,15 +599,12 @@ class _Future<T> implements Future<T> {
} else {
_chainForeignFuture(chainSource, result);
}
- if (_pendingFutureListeners == null) return;
- source = null;
- continue;
+ return;
}
}
- // Complete the future waiting for the result.
_Future result = listener.result;
- _FutureListener resultListeners = result._removeListeners();
- if (!listenerHasError) {
+ listeners = result._removeListeners();
+ if (listenerHasValue) {
result._setValue(listenerValueOrError);
} else {
AsyncError asyncError = listenerValueOrError;
@@ -701,7 +612,6 @@ class _Future<T> implements Future<T> {
}
// Prepare for next round.
source = result;
- listeners = resultListeners;
}
}
@@ -739,10 +649,3 @@ class _Future<T> implements Future<T> {
return result;
}
}
-
-class _PendingFutureListeners {
- final _Future source;
- final _PendingFutureListeners next;
- _FutureListener listeners;
- _PendingFutureListeners(this.source, this.listeners, this.next);
-}
« no previous file with comments | « no previous file | sdk/lib/async/zone.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698