| 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);
|
| -}
|
|
|