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

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: mostly tests. Created 7 years, 7 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
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..c6b70dcd8016ca37bae8a9b11a50878906694644
--- /dev/null
+++ b/sdk/lib/async/zone.dart
@@ -0,0 +1,263 @@
+// 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;
+
+get _stackTrace {
+ try {
+ throw "foo";
Lasse Reichstein Nielsen 2013/05/21 08:19:57 throw 0; No need to introduce an string constant t
floitsch 2013/05/21 18:08:32 agreed. Removed together with dPrint below.
+ } catch (e, s) {
+ return s;
+ }
+}
+
+dPrint(str) {} //=> print(str);
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Remove this? Name is bad in any case.
floitsch 2013/05/21 18:08:32 Yes. was for debugging. Removed.
+
+/**
+ * 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.
+ */
+class _Zone {
+ /// The currently running zone.
+ static _Zone _current = new _DefaultZone();
+
+ /// 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 probably be a linked list.
+ final List<_Zone> _children = <_Zone>[];
Lasse Reichstein Nielsen 2013/05/21 08:19:57 If a zone can only be a member of one zone, then y
floitsch 2013/05/21 18:08:32 Kept TODO for now, but we should decide how we wan
+
+ /// 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;
+
+ static _Zone get current => _current;
+
+ _Zone(this._parentZone) {
+ assert(_parentZone != null || this is _DefaultZone);
Lasse Reichstein Nielsen 2013/05/21 08:19:57 You could make _DefaultZone implement _Zone instea
floitsch 2013/05/21 18:08:32 There is a lot of code that is the same (dealing w
+ if (_parentZone != null) {
+ _parentZone._children.add(this);
+ }
+ }
+
+ /// 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 => _parentZone._errorZone;
+
+ void handleUncaughtError(error) {
+ _parentZone.handleUncaughtError(error);
+ }
+
+ /**
+ * Returns true if `this` and [otherZone] are in the same error zone.
+ */
+ bool inSameErrorZone(_Zone otherZone) {
+ return _errorZone == otherZone._errorZone;
+ }
+
+ /**
+ * 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() {
+ // dPrint(_stackTrace);
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Commented code.
floitsch 2013/05/21 18:08:32 Done.
+ return this;
+ }
+
+ /**
+ * Increments the open-callback counter.
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Increments the number of open callbacks. As long
floitsch 2013/05/21 18:08:32 Done.
+ *
+ * As long as the counter is not 0, the zone is considered to be running.
+ */
+ incrementOpenCallbackCount() => _openCallbacks++;
+
+ /**
+ * Decrements the open-callback counter.
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Decrements the number of open callbacks. When the
floitsch 2013/05/21 18:08:32 reworded. not exactly as you proposed, though.
+ *
+ * If the counter reaches 0, the zone is considered to be done and expects
+ * no more code to be run in the zone. In most cases it is better and easier
+ * to call [executeCallback] instead.
+ */
+ decrementOpenCallbackCount() {
+ _openCallbacks--;
+ _checkIfDone();
+ }
+
+ /**
+ * Cleans up when the zone is done.
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Cleans up this zone when it is done. This release
floitsch 2013/05/21 18:08:32 Done.
+ *
+ * A zone is done when its dynamic extent has finished executing and there
+ * are no outstanding asynchronous callbacks.
+ */
+ _onDone() {
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Don't call it _onDone when it's not a generic over
floitsch 2013/05/21 18:08:32 Done.
+ if (_parentZone != null) {
+ _parentZone._children.remove(this);
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Ok, double-linked it is. And don't act on other ob
floitsch 2013/05/21 18:08:32 added _addChild and _removeChild. Kept the list (a
+ }
+ }
+
+ void _checkIfDone() {
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Document when this is called. "After any operation
floitsch 2013/05/21 18:08:32 Done.
+ if (_openCallbacks == 0 && _children.isEmpty) {
+ dPrint("*/- done $this");
Lasse Reichstein Nielsen 2013/05/21 08:19:57 debug code.
floitsch 2013/05/21 18:08:32 Done.
+ _onDone();
+ }
+ }
+
+ /**
+ * Executes the given callback in this zone.
+ *
+ * Decrements the open-callback counter and checks (after the call) if the
+ * zone is done.
+ */
+ executeCallback(void fun()) {
Lasse Reichstein Nielsen 2013/05/21 08:19:57 void return type.
floitsch 2013/05/21 18:08:32 Done.
+ _openCallbacks--;
+ dPrint("callbacks: $_openCallbacks");
+ _runInZone(fun);
+ }
+
+ /**
+ * Same as [executeCallback] but doesn't decrement the open-callback counter.
+ */
+ executePeriodicCallback(void fun()) {
Lasse Reichstein Nielsen 2013/05/21 08:19:57 void return type. Check for more yourself.
floitsch 2013/05/21 18:08:32 Done.
+ _runInZone(fun);
+ }
+
+ _runInZone(void fun()) {
+ if (_current == this && _openCallbacks != 0) return fun();
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Consider using identical instead of ==. Just in ca
floitsch 2013/05/21 18:08:32 Done.
+ return _runGuarded(fun);
+ }
+
+ _runGuarded(void fun()) {
+ _Zone oldZone = _current;
+ _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--;
+ _current = oldZone;
+ _checkIfDone();
+ }
+ }
+
+ runAsync(void fun()) {
+ _openCallbacks++;
+ _scheduleAsyncCallback(() {
+ _openCallbacks--;
+ try {
+ _runInZone(fun);
+ } catch(e, s) {
+ handleUncaughtError(_asyncError(e, s));
+ }
+ });
+ }
+
+ // TODO(floitsch): for debugging only. Should be removed before committing.
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Reminder to do todo.
floitsch 2013/05/21 18:08:32 Done.
+ String toString() => "Zone $_id";
+ final int _id = _idCounter++;
+ static int _idCounter = 0;
+}
+
+/**
+ * The default-zone that conceptually surrounds the `main` function.
+ */
+class _DefaultZone extends _Zone {
+ _DefaultZone() : super(null);
+
+ _Zone get _errorZone => this;
+
+ handleUncaughtError(error) {
+ print("Uncaught Error: ${error}");
+ var trace = getAttachedStackTrace(error);
+ 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 _Zone {
+ 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 runWait(void fun()) {
+ if (fun == null) dPrint(_stackTrace);
+ _runInZone(() {
+ try {
+ fun();
+ } catch (e, s) {
+ handleUncaughtError(_asyncError(e, s));
+ }
+ });
+ return _doneCompleter.future;
+ }
+
+ _onDone() {
+ super._onDone();
+ dPrint("-*- $this");
+ _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) {
+ dPrint("WithError error: $error");
Lasse Reichstein Nielsen 2013/05/21 08:19:57 debug code.
floitsch 2013/05/21 18:08:32 Done.
+ errorsController.add(error);
+ }
+
+ Future runWait(void fun()) {
Lasse Reichstein Nielsen 2013/05/21 08:19:57 Name is not very telling. I.e., I would have no id
floitsch 2013/05/21 18:08:32 Changed to runWaitforCompletion.
+ super.runWait(fun).whenComplete(() {
+ dPrint("closing");
Lasse Reichstein Nielsen 2013/05/21 08:19:57 debug code.
floitsch 2013/05/21 18:08:32 Done.
+ errorsController.close();
+ });
+ dPrint("zone: ${_Zone._current}");
+ }
+
+ String toString() => "WithErrors ${super.toString()}";
+}
+
+Stream catchErrors(void body()) {
+ _Zone catchErrorsZone = new _CatchErrorsZone(_Zone._current);
+ catchErrorsZone.runWait(body);
+ return catchErrorsZone.errors;
+}
+
+Future waitForCompletion(void body()) {
+ _Zone zone = new _WaitForCompletionZone(_Zone._current);
+ return zone.runWait(body);
+}

Powered by Google App Engine
This is Rietveld 408576698