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

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

Issue 14973006: Zone support for Futures. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase 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/future_impl.dart ('k') | tests/lib/async/catch_errors2_test.dart » ('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..ae88759457757217882cf4f3ab78a1b9e3cd1e71
--- /dev/null
+++ b/sdk/lib/async/zone.dart
@@ -0,0 +1,330 @@
+// 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 [unexpectCallback] 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 [unexpectCallback] when the [Timer] is canceled.
+ */
+ void unexpectCallback();
+
+ /**
+ * 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 does not decrement the number of
+ * callbacks this zone is waiting for (see [expectCallback]).
+ */
+ void executePeriodicCallback(void fun());
+
+ /**
+ * Runs [fun] asynchronously in this zone.
+ */
+ void runAsync(void fun());
+
+ /**
+ * 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;
+
+ _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++;
+
+ unexpectCallback() {
+ _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 (_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--;
+ _runInZone(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but doesn't decrement the open-callback counter.
+ */
+ void executePeriodicCallback(void fun()) {
+ _runInZone(fun);
+ }
+
+ _runInZone(fun()) {
+ if (identical(_Zone._current, this) && _openCallbacks != 0) return fun();
+ return _runGuarded(fun);
+ }
+
+ _runGuarded(void 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.
+ _openCallbacks++;
+ try {
+ return fun();
+ } finally {
+ _openCallbacks--;
+ _Zone._current = oldZone;
+ _checkIfDone();
+ }
+ }
+
+ runAsync(void fun()) {
+ _openCallbacks++;
+ _scheduleAsyncCallback(() {
+ _openCallbacks--;
+ try {
+ _runInZone(fun);
+ } catch(e, s) {
+ handleUncaughtError(_asyncError(e, s));
+ }
+ });
+ }
+
+ 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;
+ });
+ }
+}
+
+/**
+ * A zone that can execute a callback (through a future) when the zone is dead.
+ */
+class _WaitForCompletionZone extends _ZoneBase {
+ final Completer _doneCompleter = new Completer();
+
+ _WaitForCompletionZone(_Zone parentZone) : super(parentZone);
+
+ /**
+ * Runs the given function asynchronously and returns a future that is
+ * completed with `null` once the zone is done.
+ */
+ Future runWaitForCompletion(void fun()) {
+ _runInZone(() {
+ try {
+ fun();
+ } catch (e, s) {
+ handleUncaughtError(_asyncError(e, s));
+ }
+ });
+ return _doneCompleter.future;
+ }
+
+ _dispose() {
+ super._dispose();
+ _doneCompleter.complete();
+ }
+
+ String toString() => "WaitForCompletion ${super.toString()}";
+}
+
+/**
+ * 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 StreamController errorsController = new StreamController();
+
+ Stream get errors => errorsController.stream;
+
+ _CatchErrorsZone(_Zone parentZone) : super(parentZone);
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ errorsController.add(error);
+ }
+
+ Future runWaitForCompletion(void fun()) {
+ super.runWaitForCompletion(fun).whenComplete(() {
+ errorsController.close();
+ });
+ }
+
+ String toString() => "WithErrors ${super.toString()}";
+}
+
+Stream catchErrors(void body()) {
+ _Zone catchErrorsZone = new _CatchErrorsZone(_Zone._current);
+ catchErrorsZone.runWaitForCompletion(body);
+ return catchErrorsZone.errors;
+}
+
+Future waitForCompletion(void body()) {
+ _Zone zone = new _WaitForCompletionZone(_Zone._current);
+ return zone.runWaitForCompletion(body);
+}
« no previous file with comments | « sdk/lib/async/future_impl.dart ('k') | tests/lib/async/catch_errors2_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698