Chromium Code Reviews| Index: pkg/analyzer/lib/src/cancelable_future.dart |
| diff --git a/pkg/analyzer/lib/src/cancelable_future.dart b/pkg/analyzer/lib/src/cancelable_future.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..34226c4eb1ed4dd27ccfa8f8ffd001f493b25461 |
| --- /dev/null |
| +++ b/pkg/analyzer/lib/src/cancelable_future.dart |
| @@ -0,0 +1,230 @@ |
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +library cancelable_future; |
| + |
| +import 'dart:async'; |
| + |
| +/** |
| + * Type of callback called when the future returned by a CancelableCompleter |
| + * is canceled. |
| + */ |
| +typedef void CancelHandler(); |
| + |
| +class CancelableCompleter<T> implements Completer<T> { |
|
Brian Wilkerson
2014/12/15 19:49:26
I'd like to see doc comments for all of the classe
Paul Berry
2014/12/16 16:58:06
Done.
|
| + /** |
| + * The completer which holds the state of the computation. If the |
| + * computation is canceled, this completer will remain in the non-completed |
| + * state. |
| + */ |
| + final Completer<T> _innerCompleter = new Completer<T>.sync(); |
| + |
| + /** |
| + * The completer which holds the future that is exposed to the client |
| + * through [future]. If the computation is canceled, this completer will |
| + * be completed with a FutureCanceledError. |
| + */ |
| + final Completer<T> _outerCompleter = new Completer<T>(); |
| + |
| + /** |
| + * The callback to invoke if the 'cancel' method is called on the future |
| + * returned by [future]. This callback will only be invoked if the future |
| + * is canceled before being completed. |
| + */ |
| + final CancelHandler _onCancel; |
| + |
| + _CancelableCompleterFuture<T> _future; |
| + |
| + /** |
| + * Create a CancelableCompleter that will invoke the given callback |
| + * synchronously if its future is canceled. The callback will not be |
| + * invoked if the future is completed before being canceled. |
|
Brian Wilkerson
2014/12/15 19:49:26
And the future will be completed with an error bef
|
| + */ |
| + CancelableCompleter(this._onCancel) { |
| + _future = new _CancelableCompleterFuture<T>(this); |
| + |
| + // When the client completes the inner completer, we need to check whether |
| + // the outer completer has been completed. If it has, then the operation |
| + // was canceled before it finished, and it's too late to un-cancel it, so |
| + // we just ignore the result from the inner completer. If it hasn't, then |
| + // we simply pass along the result from the inner completer to the outer |
| + // completer. |
| + // |
| + // Note that the reason it is safe for the inner completer to be |
| + // synchronous is that we don't expose its future to client code, and we |
| + // only use it to complete the outer completer (which is asynchronous). |
| + _innerCompleter.future.then((T value) { |
| + if (!_outerCompleter.isCompleted) { |
| + _outerCompleter.complete(value); |
| + } |
| + }, onError: (Object error, StackTrace stackTrace) { |
| + if (!_outerCompleter.isCompleted) { |
| + _outerCompleter.completeError(error, stackTrace); |
| + } |
| + }); |
| + } |
| + |
| + @override |
| + CancelableFuture<T> get future => _future; |
| + |
| + @override |
| + bool get isCompleted => _innerCompleter.isCompleted; |
| + |
| + @override |
| + void complete([value]) { |
| + _innerCompleter.complete(value); |
| + } |
| + |
| + @override |
| + void completeError(Object error, [StackTrace stackTrace]) { |
| + _innerCompleter.completeError(error, stackTrace); |
| + } |
| + |
| + void _cancel() { |
| + if (!_outerCompleter.isCompleted) { |
| + _outerCompleter.completeError(new FutureCanceledError()); |
| + _onCancel(); |
| + } |
| + } |
| +} |
| + |
| +/** |
| + * An object representing a delayed computation that can be canceled. |
| + */ |
| +abstract class CancelableFuture<T> implements Future<T> { |
| + /** |
| + * A CancelableFuture containing the result of calling [computation] |
| + * asynchronously. Since the computation is started without delay, calling |
| + * the future's cancel method will have no effect. |
| + */ |
| + factory CancelableFuture(computation()) => |
| + new _WrappedFuture<T>(new Future<T>(computation)); |
| + |
| + /** |
| + * A CancelableFuture containing the result of calling [computation] after |
| + * [duration] has passed. |
| + * |
| + * TODO(paulberry): if the future is canceled before the duration has |
| + * elapsed, the computation should not be performed. |
| + */ |
| + factory CancelableFuture.delayed(Duration duration, [computation()]) => |
| + new _WrappedFuture<T>(new Future<T>.delayed(duration, computation)); |
| + |
| + /** |
| + * A CancelableFuture that completes with error. Since the future is |
| + * completed without delay, calling the future's cancel method will have no |
| + * effect. |
| + */ |
| + factory CancelableFuture.error(Object error, [StackTrace stackTrace]) => |
| + new _WrappedFuture<T>(new Future<T>.error(error, stackTrace)); |
| + |
| + /** |
| + * A CancelableFuture containing the result of calling [computation] |
| + * asynchronously with scheduleMicrotask. Since the computation is started |
| + * without delay, calling the future's cancel method will have no effect. |
| + */ |
| + factory CancelableFuture.microtask(computation()) => |
| + new _WrappedFuture<T>(new Future<T>.microtask(computation)); |
| + |
| + /** |
| + * A CancelableFuture containing the result of immediately calling |
| + * [computation]. Since the computation is started without delay, calling |
| + * the future's cancel method will have no effect. |
| + */ |
| + factory CancelableFuture.sync(computation()) => |
| + new _WrappedFuture<T>(new Future<T>.sync(computation)); |
| + |
| + /** |
| + * A CancelableFuture whose value is available in the next event-loop |
| + * iteration. Since the value is available without delay, calling the |
| + * future's cancel method will have no effect. |
| + */ |
| + factory CancelableFuture.value([value]) => |
| + new _WrappedFuture<T>(new Future<T>.value(value)); |
| + |
| + /** |
| + * If the delayed computation has not yet completed, attempt to cancel it. |
| + * Note that the cancellation is not always possible. If the computation |
| + * could be canceled, the future is completed with a FutureCanceledError. |
| + * Otherwise it will behave as though cancel() was not called. |
| + * |
| + * Note that attempting to cancel a future that has already completed will |
| + * never succeed--futures that have already completed retain their final |
| + * state forever. |
| + */ |
| + void cancel(); |
| +} |
| + |
| +class FutureCanceledError { |
| +} |
| + |
| +class _CancelableCompleterFuture<T> implements CancelableFuture<T> { |
| + final CancelableCompleter<T> _completer; |
| + |
| + _CancelableCompleterFuture(this._completer); |
| + |
| + @override |
| + Stream<T> asStream() { |
| + // TODO(paulberry): Implement this in such a way that |
| + // StreamSubscription.cancel() cancels the future. |
| + return _completer._outerCompleter.future.asStream(); |
| + } |
| + |
| + @override |
| + void cancel() { |
| + _completer._cancel(); |
| + } |
| + |
| + @override |
| + Future catchError(Function onError, {bool test(Object error)}) => |
| + _completer._outerCompleter.future.catchError(onError, test: test); |
| + |
| + @override |
| + Future then(onValue(T value), {Function onError}) => |
| + _completer._outerCompleter.future.then(onValue, onError: onError); |
| + |
| + @override |
| + Future timeout(Duration timeLimit, {onTimeout()}) { |
| + // TODO(paulberry): Implement this in such a way that a timeout cancels |
| + // the future. |
| + return _completer._outerCompleter.future.timeout( |
| + timeLimit, |
| + onTimeout: onTimeout); |
| + } |
| + |
| + @override |
| + Future<T> whenComplete(action()) => |
| + _completer._outerCompleter.future.whenComplete(action); |
| +} |
| + |
| +/** |
| + * A CancelableFuture that wraps an ordinary Future. Attempting to cancel a |
| + * _WrappedFuture has no effect. |
| + */ |
| +class _WrappedFuture<T> implements CancelableFuture<T> { |
| + final Future<T> _future; |
| + |
| + _WrappedFuture(this._future); |
| + |
| + @override |
| + Stream asStream() => _future.asStream(); |
| + |
| + @override |
| + void cancel() {} |
| + |
| + @override |
| + Future catchError(Function onError, {bool test(Object error)}) => |
| + _future.catchError(onError, test: test); |
| + |
| + @override |
| + Future then(onValue(value), {Function onError}) => |
| + _future.then(onValue, onError: onError); |
| + |
| + @override |
| + Future timeout(Duration timeLimit, {onTimeout()}) => |
| + _future.timeout(timeLimit, onTimeout: onTimeout); |
| + |
| + @override |
| + Future whenComplete(action()) => _future.whenComplete(action); |
| +} |