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

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

Issue 17098007: Reapply "Zone support for Futures, Timer, Streams." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/async/timer.dart ('k') | tests/isolate/isolate.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/async/zone.dart
diff --git a/sdk/lib/async/zone.dart b/sdk/lib/async/zone.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b92aa1634b2492cec89987fb993a3726f2301628
--- /dev/null
+++ b/sdk/lib/async/zone.dart
@@ -0,0 +1,504 @@
+// Copyright (c) 2013, 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.
+
+part of dart.async;
+
+/**
+ * A Zone represents the asynchronous version of a dynamic extent. Asynchronous
+ * callbacks are executed in the zone they have been queued in. For example,
+ * the callback of a `future.then` is executed in the same zone as the one where
+ * the `then` was invoked.
+ */
+abstract class _Zone {
+ /// The currently running zone.
+ static _Zone _current = new _DefaultZone();
+
+ static _Zone get current => _current;
+
+ void handleUncaughtError(error);
+
+ /**
+ * Returns true if `this` and [otherZone] are in the same error zone.
+ */
+ bool inSameErrorZone(_Zone otherZone);
+
+ /**
+ * Returns a zone for reentry in the zone.
+ *
+ * The returned zone is equivalent to `this` (and frequently is indeed
+ * `this`).
+ *
+ * The main purpose of this method is to allow `this` to attach debugging
+ * information to the returned zone.
+ */
+ _Zone fork();
+
+ /**
+ * Tells the zone that it needs to wait for one more callback before it is
+ * done.
+ *
+ * Use [executeCallback] or [cancelCallbackExpectation] when the callback is executed
+ * (or canceled).
+ */
+ void expectCallback();
+
+ /**
+ * Tells the zone not to wait for a callback anymore.
+ *
+ * Prefer calling [executeCallback], instead. This method is mostly useful
+ * for repeated callbacks (for example with [Timer.periodic]). In this case
+ * one should should call [expectCallback] when the repeated callback is
+ * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled.
+ */
+ void cancelCallbackExpectation();
+
+ /**
+ * Executes the given callback in this zone.
+ *
+ * Decrements the number of callbacks this zone is waiting for (see
+ * [expectCallback]).
+ */
+ void executeCallback(void fun());
+
+ /**
+ * Same as [executeCallback] but catches uncaught errors and gives them to
+ * [handleUncaughtError].
+ */
+ void executeCallbackGuarded(void fun());
+
+ /**
+ * Same as [executeCallback] but does not decrement the number of
+ * callbacks this zone is waiting for (see [expectCallback]).
+ */
+ void executePeriodicCallback(void fun());
+
+ /**
+ * Same as [executePeriodicCallback] but catches uncaught errors and gives
+ * them to [handleUncaughtError].
+ */
+ void executePeriodicCallbackGuarded(void fun());
+
+ /**
+ * Runs [fun] asynchronously in this zone.
+ */
+ void runAsync(void fun());
+
+ /**
+ * Creates a Timer where the callback is executed in this zone.
+ */
+ Timer createTimer(Duration duration, void callback());
+
+ /**
+ * Creates a periodic Timer where the callback is executed in this zone.
+ */
+ Timer createPeriodicTimer(Duration duration, void callback(Timer timer));
+
+ /**
+ * The error zone is the one that is responsible for dealing with uncaught
+ * errors. Errors are not allowed to cross zones with different error-zones.
+ */
+ _Zone get _errorZone;
+
+ /**
+ * Adds [child] as a child of `this`.
+ *
+ * This usually means that the [child] is in the asynchronous dynamic extent
+ * of `this`.
+ */
+ void _addChild(_Zone child);
+
+ /**
+ * Removes [child] from `this`' children.
+ *
+ * This usually means that the [child] has finished executing and is done.
+ */
+ void _removeChild(_Zone child);
+}
+
+/**
+ * Basic implementation of a [_Zone]. This class is intended for subclassing.
+ */
+class _ZoneBase implements _Zone {
+ /// The parent zone. [null] if `this` is the default zone.
+ final _Zone _parentZone;
+
+ /// The children of this zone. A child's [_parentZone] is `this`.
+ // TODO(floitsch): this should be a double-linked list.
+ final List<_Zone> _children = <_Zone>[];
+
+ /// The number of outstanding (asynchronous) callbacks. As long as the
+ /// number is greater than 0 it means that the zone is not done yet.
+ int _openCallbacks = 0;
+
+ bool _isExecutingCallback = false;
+
+ _ZoneBase(this._parentZone) {
+ _parentZone._addChild(this);
+ }
+
+ _ZoneBase._defaultZone() : _parentZone = null {
+ assert(this is _DefaultZone);
+ }
+
+ _Zone get _errorZone => _parentZone._errorZone;
+
+ void handleUncaughtError(error) {
+ _parentZone.handleUncaughtError(error);
+ }
+
+ bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
+
+ _Zone fork() => this;
+
+ expectCallback() => _openCallbacks++;
+
+ cancelCallbackExpectation() {
+ _openCallbacks--;
+ _checkIfDone();
+ }
+
+ /**
+ * Cleans up this zone when it is done.
+ *
+ * This releases internal memore structures that are no longer necessary.
+ *
+ * A zone is done when its dynamic extent has finished executing and
+ * there are no outstanding asynchronous callbacks.
+ */
+ _dispose() {
+ if (_parentZone != null) {
+ _parentZone._removeChild(this);
+ }
+ }
+
+ /**
+ * Checks if the zone is done and doesn't have any outstanding callbacks
+ * anymore.
+ *
+ * This method is called when an operation has decremented the
+ * outstanding-callback count, or when a child has been removed.
+ */
+ void _checkIfDone() {
+ if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) {
+ _dispose();
+ }
+ }
+
+ /**
+ * Executes the given callback in this zone.
+ *
+ * Decrements the open-callback counter and checks (after the call) if the
+ * zone is done.
+ */
+ void executeCallback(void fun()) {
+ _openCallbacks--;
+ this._runUnguarded(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but catches uncaught errors and gives them to
+ * [handleUncaughtError].
+ */
+ void executeCallbackGuarded(void fun()) {
+ _openCallbacks--;
+ this._runGuarded(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but doesn't decrement the open-callback counter.
+ */
+ void executePeriodicCallback(void fun()) {
+ this._runUnguarded(fun);
+ }
+
+ /**
+ * Same as [executePeriodicCallback] but catches uncaught errors and gives
+ * them to [handleUncaughtError].
+ */
+ void executePeriodicCallbackGuarded(void fun()) {
+ this._runGuarded(fun);
+ }
+
+ _runInZone(fun(), bool handleUncaught) {
+ if (identical(_Zone._current, this)
+ && !handleUncaught
+ && _isExecutingCallback) {
+ // No need to go through a try/catch.
+ return fun();
+ }
+
+ _Zone oldZone = _Zone._current;
+ _Zone._current = this;
+ // While we are executing the function we don't want to have other
+ // synchronous calls to think that they closed the zone. By incrementing
+ // the _openCallbacks count we make sure that their test will fail.
+ // As a side effect it will make nested calls faster since they are
+ // (probably) in the same zone and have an _openCallbacks > 0.
+ bool oldIsExecuting = _isExecutingCallback;
+ _isExecutingCallback = true;
+ // TODO(430): remove second try when VM bug is fixed.
+ try {
+ try {
+ return fun();
+ } catch(e, s) {
+ if (handleUncaught) {
+ handleUncaughtError(_asyncError(e, s));
+ } else {
+ rethrow;
+ }
+ }
+ } finally {
+ _isExecutingCallback = oldIsExecuting;
+ _Zone._current = oldZone;
+ _checkIfDone();
+ }
+ }
+
+ /**
+ * Runs the function and catches uncaught errors.
+ *
+ * Uncaught errors are given to [handleUncaughtError].
+ */
+ _runGuarded(void fun()) {
+ return _runInZone(fun, true);
+ }
+
+ /**
+ * Runs the function but doesn't catch uncaught errors.
+ */
+ _runUnguarded(void fun()) {
+ return _runInZone(fun, false);
+ }
+
+ runAsync(void fun()) {
+ _openCallbacks++;
+ _scheduleAsyncCallback(() {
+ _openCallbacks--;
+ _runGuarded(fun);
+ });
+ }
+
+ Timer createTimer(Duration duration, void callback()) {
+ return new _ZoneTimer(this, duration, callback);
+ }
+
+ Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
+ return new _PeriodicZoneTimer(this, duration, callback);
+ }
+
+ void _addChild(_Zone child) {
+ _children.add(child);
+ }
+
+ void _removeChild(_Zone child) {
+ assert(!_children.isEmpty);
+ // Children are usually added and removed fifo or filo.
+ if (identical(_children.last, child)) {
+ _children.length--;
+ _checkIfDone();
+ return;
+ }
+ for (int i = 0; i < _children.length; i++) {
+ if (identical(_children[i], child)) {
+ _children[i] = _children[_children.length - 1];
+ _children.length--;
+ // No need to check for done, as otherwise _children.last above would
+ // have triggered.
+ assert(!_children.isEmpty);
+ return;
+ }
+ }
+ throw new ArgumentError(child);
+ }
+}
+
+/**
+ * The default-zone that conceptually surrounds the `main` function.
+ */
+class _DefaultZone extends _ZoneBase {
+ _DefaultZone() : super._defaultZone();
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ _scheduleAsyncCallback(() {
+ print("Uncaught Error: ${error}");
+ var trace = getAttachedStackTrace(error);
+ _attachStackTrace(error, null);
+ if (trace != null) {
+ print("Stack Trace:\n$trace\n");
+ }
+ throw error;
+ });
+ }
+}
+
+typedef void _CompletionCallback();
+
+/**
+ * A zone that executes a callback when the zone is dead.
+ */
+class _WaitForCompletionZone extends _ZoneBase {
+ final _CompletionCallback _onDone;
+
+ _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
+
+ /**
+ * Runs the given function asynchronously. Executes the [_onDone] callback
+ * when the zone is done.
+ */
+ runWaitForCompletion(void fun()) {
+ return this._runUnguarded(fun);
+ }
+
+ _dispose() {
+ super._dispose();
+ _onDone();
+ }
+
+ String toString() => "WaitForCompletion ${super.toString()}";
+}
+
+typedef void _HandleErrorCallback(error);
+
+/**
+ * A zone that collects all uncaught errors and provides them in a stream.
+ * The stream is closed when the zone is done.
+ */
+class _CatchErrorsZone extends _WaitForCompletionZone {
+ final _HandleErrorCallback _handleError;
+
+ _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone())
+ : super(parentZone, onDone);
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ try {
+ _handleError(error);
+ } catch(e, s) {
+ if (identical(e, s)) {
+ _parentZone.handleUncaughtError(error);
+ } else {
+ _parentZone.handleUncaughtError(_asyncError(e, s));
+ }
+ }
+ }
+
+ /**
+ * Runs the given function asynchronously. Executes the [_onDone] callback
+ * when the zone is done.
+ */
+ runWaitForCompletion(void fun()) {
+ return this._runGuarded(fun);
+ }
+
+ String toString() => "WithErrors ${super.toString()}";
+}
+
+typedef void _TimerCallback();
+
+/**
+ * A [Timer] class that takes zones into account.
+ */
+class _ZoneTimer implements Timer {
+ final _Zone _zone;
+ final _TimerCallback _callback;
+ Timer _timer;
+ bool _isDone = false;
+
+ _ZoneTimer(this._zone, Duration duration, this._callback) {
+ _zone.expectCallback();
+ _timer = _createTimer(duration, this.run);
+ }
+
+ void run() {
+ _isDone = true;
+ _zone.executeCallbackGuarded(_callback);
+ }
+
+ void cancel() {
+ if (!_isDone) _zone.cancelCallbackExpectation();
+ _isDone = true;
+ _timer.cancel();
+ }
+}
+
+typedef void _PeriodicTimerCallback(Timer timer);
+
+/**
+ * A [Timer] class for periodic callbacks that takes zones into account.
+ */
+class _PeriodicZoneTimer implements Timer {
+ final _Zone _zone;
+ final _PeriodicTimerCallback _callback;
+ Timer _timer;
+ bool _isDone = false;
+
+ _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
+ _zone.expectCallback();
+ _timer = _createPeriodicTimer(duration, this.run);
+ }
+
+ void run(Timer timer) {
+ assert(identical(_timer, timer));
+ _zone.executePeriodicCallbackGuarded(() { _callback(this); });
+ }
+
+ void cancel() {
+ if (!_isDone) _zone.cancelCallbackExpectation();
+ _isDone = true;
+ _timer.cancel();
+ }
+}
+
+/**
+ * Runs [body] in its own zone.
+ *
+ * If [onError] is non-null the zone is considered an error zone. All uncaught
+ * errors, synchronous or asynchronous, in the zone are caught and handled
+ * by the callback.
+ *
+ * [onDone] (if non-null) is invoked when the zone has no more outstanding
+ * callbacks.
+ *
+ * Examples:
+ *
+ * runZonedExperimental(() {
+ * new Future(() { throw "asynchronous error"; });
+ * }, onError: print); // Will print "asynchronous error".
+ *
+ * The following example prints "1", "2", "3", "4" in this order.
+ *
+ * runZonedExperimental(() {
+ * print(1);
+ * new Future.value(3).then(print);
+ * }, onDone: () { print(4); });
+ * print(2);
+ *
+ * Errors may never cross error-zone boundaries. This is intuitive for leaving
+ * a zone, but it also applies for errors that would enter an error-zone.
+ * Errors that try to cross error-zone boundaries are considered uncaught.
+ *
+ * var future = new Future.value(499);
+ * runZonedExperimental(() {
+ * future = future.then((_) { throw "error in first error-zone"; });
+ * runZonedExperimental(() {
+ * future = future.catchError((e) { print("Never reached!"); });
+ * }, onError: (e) { print("unused error handler"); });
+ * }, onError: (e) { print("catches error of first error-zone."); });
+ *
+ */
+runZonedExperimental(body(), { void onError(error), void onDone() }) {
+ // TODO(floitsch): we probably still want to install a new Zone.
+ if (onError == null && onDone == null) return body();
+ if (onError == null) {
+ _WaitForCompletionZone zone =
+ new _WaitForCompletionZone(_Zone._current, onDone);
+ return zone.runWaitForCompletion(body);
+ }
+ if (onDone == null) onDone = _nullDoneHandler;
+ _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
+ return zone.runWaitForCompletion(body);
+}
« no previous file with comments | « sdk/lib/async/timer.dart ('k') | tests/isolate/isolate.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698