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

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

Issue 23875032: Expose Zones. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Mark stack trace test as failing. Created 7 years, 3 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
index 87741a0bc23ebf39d1877d81cfa560d451036e5e..817b453a7a5718778f4f21344d29dc264bed78bc 100644
--- a/sdk/lib/async/zone.dart
+++ b/sdk/lib/async/zone.dart
@@ -4,101 +4,265 @@
part of dart.async;
+typedef dynamic ZoneCallback();
+typedef dynamic ZoneCallback1(arg);
+
+typedef dynamic HandleUncaughtErrorHandler(
+ Zone self, ZoneDelegate parent, Zone zone, e);
+typedef dynamic RunHandler(Zone self, ZoneDelegate parent, Zone zone, f());
+typedef dynamic Run1Handler(
+ Zone self, ZoneDelegate parent, Zone zone, f(arg), arg);
+typedef ZoneCallback RegisterCallbackHandler(
+ Zone self, ZoneDelegate parent, Zone zone, f());
+typedef ZoneCallback1 RegisterCallback1Handler(
+ Zone self, ZoneDelegate parent, Zone zone, f(arg));
+typedef void ScheduleMicrotaskHandler(
+ Zone self, ZoneDelegate parent, Zone zone, f());
+typedef Timer CreateTimerHandler(
+ Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
+typedef Timer CreatePeriodicTimerHandler(
+ Zone self, ZoneDelegate parent, Zone zone,
+ Duration period, void f(Timer timer));
+typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
+ Map<Symbol, dynamic> zoneValues,
+ ZoneDescription description);
+
+/**
+ * This class provides a description for a forked zone.
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Description seems like a passive thing, not an act
floitsch 2013/09/23 17:12:07 Went with specification
+ *
+ * When forking a new zone (see [Zone.fork]) one can override the default
+ * behavior of the zone by providing callbacks. These callbacks must be
+ * given in an instance of this class.
+ *
+ * Handlers have the same signature as the same-named methods on [Zone] but
+ * receive three additional arguments:
+ *
+ * 1. the zone the handlers are attached to (the "self" zone).
+ * 2. a [ZoneDelegate] to the parent zone.
+ * 3. the zone that first received the request (before the request was
+ * bubbled down).
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Perplexing, but I feel like bubbles should go up.
floitsch 2013/09/23 17:12:07 I guess not. Probably more correct. done.
+ *
+ * Handlers can either intercept the request (by simply not calling the
Lasse Reichstein Nielsen 2013/09/23 14:24:12 intercept -> stop propagating It always intercept
floitsch 2013/09/23 17:12:07 Done.
+ * parent handler), or forward to the parent zone, potentially modifying the
+ * arguments on the way.
+ */
+abstract class ZoneDescription {
+ const factory ZoneDescription({
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Document constructor. Just something along the lin
floitsch 2013/09/23 17:12:07 Done.
+ void handleUncaughtError(
+ Zone self, ZoneDelegate parent, Zone zone, e): null,
+ dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ dynamic run1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg): null,
+ ZoneCallback registerCallback(
+ Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ ZoneCallback1 registerCallback1(
+ Zone self, ZoneDelegate parent, Zone zone, f(arg)): null,
+ void scheduleMicrotask(
+ Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ Timer createTimer(Zone self, ZoneDelegate parent, Zone zone,
+ Duration duration, void f()): null,
+ Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
+ Duration period, void f(Timer timer)): null,
+ Zone fork(Zone self, ZoneDelegate parent, Zone zone,
+ Map zoneValues, ZoneDescription description): null
+ }) = _ZoneDescription;
+
+ factory ZoneDescription.from(ZoneDescription other, {
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Documentation, something like: Creates description
floitsch 2013/09/23 17:12:07 Done.
+ void handleUncaughtError(
+ Zone self, ZoneDelegate parent, Zone zone, e): null,
+ dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ dynamic run1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg): null,
+ ZoneCallback registerCallback(
+ Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ ZoneCallback1 registerCallback1(
+ Zone self, ZoneDelegate parent, Zone zone, f(arg)): null,
+ void scheduleMicrotask(
+ Zone self, ZoneDelegate parent, Zone zone, f()): null,
+ Timer createTimer(Zone self, ZoneDelegate parent, Zone zone,
+ Duration duration, void f()): null,
+ Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
+ Duration period, void f(Timer timer)): null,
+ Zone fork(Zone self, ZoneDelegate parent, Zone zone,
+ Map<Symbol, dynamic> zoneValues,
+ ZoneDescription description): null
+ }) {
+ return new ZoneDescription(
+ handleUncaughtError: handleUncaughtError != null
+ ? handleUncaughtError
+ : other.handleUncaughtError,
+ run: run != null ? run : other.run,
+ run1: run1 != null ? run1 : other.run1,
+ registerCallback: registerCallback != null
+ ? registerCallback
+ : other.registerCallback,
+ registerCallback1: registerCallback1 != null
+ ? registerCallback1
+ : other.registerCallback1,
+ scheduleMicrotask: scheduleMicrotask != null
+ ? scheduleMicrotask
+ : other.scheduleMicrotask,
+ createTimer : createTimer != null ? createTimer : other.createTimer,
+ createPeriodicTimer: createPeriodicTimer != null
+ ? createPeriodicTimer
+ : other.createPeriodicTimer,
+ fork: fork != null ? fork : other.fork);
+ }
+
+ HandleUncaughtErrorHandler get handleUncaughtError;
+ RunHandler get run;
+ Run1Handler get run1;
+ RegisterCallbackHandler get registerCallback;
+ RegisterCallback1Handler get registerCallback1;
+ ScheduleMicrotaskHandler get scheduleMicrotask;
+ CreateTimerHandler get createTimer;
+ CreatePeriodicTimerHandler get createPeriodicTimer;
+ ForkHandler get fork;
+}
+
+/**
+ * Internal [ZoneDescription] class.
+ *
+ * The implementation wants to rely on the fact that the getters cannot change
+ * dynamically. We thus require users to go through the redirecting
+ * [ZoneDescription] constructor which instantiates this class.
+ */
+class _ZoneDescription implements ZoneDescription {
+ const _ZoneDescription({
+ this.handleUncaughtError: null,
+ this.run: null,
+ this.run1: null,
+ this.registerCallback: null,
+ this.registerCallback1: null,
+ this.scheduleMicrotask: null,
+ this.createTimer: null,
+ this.createPeriodicTimer: null,
+ this.fork: null
+ });
+
+ // TODO(13406): Enable types when dart2js supports it.
+ final /*HandleUncaughtErrorHandler*/ handleUncaughtError;
+ final /*RunHandler*/ run;
+ final /*Run1Handler*/ run1;
+ final /*RegisterCallbackHandler*/ registerCallback;
+ final /*RegisterCallback1Handler*/ registerCallback1;
+ final /*ScheduleMicrotaskHandler*/ scheduleMicrotask;
+ final /*CreateTimerHandler*/ createTimer;
+ final /*CreatePeriodicTimerHandler*/ createPeriodicTimer;
+ final /*ForkHandler*/ fork;
+}
+
+/**
+ * This class allows to delegate callbacks to a parent zone.
Lasse Reichstein Nielsen 2013/09/23 14:24:12 allows to delegate -> delegates Or either "allows
floitsch 2013/09/23 17:12:07 "This class wraps zones for delegation".
+ *
+ * When forwarding to parent zones one can't just invoke the parent zone's
+ * exposed functions (like [Zone.run]), but one needs to provide more
+ * information (like the zone the `run` was initiated). Zone callbacks thus
+ * receive more information including this [ZoneDelegate] class. When delegating
+ * to the parent zone one should go through the given instance instead of
+ * directly invoking the parent zone.
+ */
+abstract class ZoneDelegate {
+ /// The [Zone] this class wraps.
+ Zone get zone;
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Can you avoid making the zone public? That would a
floitsch 2013/09/23 17:12:07 Done. But it won't change much. The Zone itself ex
+
+ dynamic handleUncaughtError(Zone zone, e);
+ dynamic run(Zone zone, f());
+ dynamic run1(Zone zone, f(arg), arg);
+ ZoneCallback registerCallback(Zone zone, f());
+ ZoneCallback1 registerCallback1(Zone zone, f(arg));
+ void scheduleMicrotask(Zone zone, f());
+ Timer createTimer(Zone zone, Duration duration, void f());
+ Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
+ Zone fork(Zone zone, Map zoneValues, ZoneDescription description);
+}
+
/**
* 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 {
+abstract class Zone {
+ // Private constructor so that it is not possible instantiate a Zone class.
+ Zone._();
+
+ /// The root zone that is implicitly created.
+ static const Zone ROOT = _ROOT_ZONE;
+
/// The currently running zone.
- static _Zone _current = new _DefaultZone();
+ static Zone _current = _ROOT_ZONE;
- static _Zone get current => _current;
+ static Zone get current => _current;
- void handleUncaughtError(error);
+ dynamic handleUncaughtError(error);
/**
- * Returns true if `this` and [otherZone] are in the same error zone.
+ * Returns the parent zone.
+ *
+ * Returns `null` if `this` is the [ROOT] zone.
*/
- bool inSameErrorZone(_Zone otherZone);
+ Zone get parent;
/**
- * Returns a zone for reentry in the zone.
- *
- * The returned zone is equivalent to `this` (and frequently is indeed
- * `this`).
+ * Returns true if `this` and [otherZone] are in the same error zone.
*
- * The main purpose of this method is to allow `this` to attach debugging
- * information to the returned zone.
+ * Two zones are in the same error zone if they share the same
+ * [handleUncaughtError] callback.
*/
- _Zone fork();
+ bool inSameErrorZone(Zone otherZone);
/**
- * 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).
+ * Creates a new zone as a child of `this`.
*/
- void expectCallback();
+ Zone fork([Map<Symbol, dynamic> zoneValues, ZoneDescription description]);
/**
- * 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.
+ * Executes the given function [f] in this zone.
*/
- void cancelCallbackExpectation();
+ dynamic run(f());
/**
- * Executes the given callback [f] in this zone.
- *
- * Decrements the number of callbacks this zone is waiting for (see
- * [expectCallback]).
+ * Executes the given callback [f] with argument [arg] in this zone.
*/
- void executeCallback(void f());
+ dynamic run1(f(arg), var arg);
/**
- * Same as [executeCallback] but catches uncaught errors and gives them to
+ * Executes the given function [f] in this zone.
+ *
+ * Same as [run] but catches uncaught errors and gives them to
* [handleUncaughtError].
*/
- void executeCallbackGuarded(void f());
+ dynamic runGuarded(f());
/**
- * Same as [executeCallback] but does not decrement the number of
- * callbacks this zone is waiting for (see [expectCallback]).
+ * Executes the given callback [f] in this zone.
+ *
+ * Same as [run1] but catches uncaught errors and gives them to
+ * [handleUncaughtError].
*/
- void executePeriodicCallback(void f());
+ dynamic runGuarded1(f(arg), var arg);
- /**
- * Same as [executePeriodicCallback] but catches uncaught errors and gives
- * them to [handleUncaughtError].
- */
- void executePeriodicCallbackGuarded(void f());
+ ZoneCallback registerCallback(f());
+ ZoneCallback1 registerCallback1(f(arg));
/**
- * Executes [f] in `this` zone.
- *
- * The behavior of this method should be the same as
- * [executePeriodicCallback] except that it can have a return value.
+ * Equivalent to:
*
- * Returns the result of the invocation.
+ * ZoneCallback registered = registerCallback(f);
+ * return () => this.run(registered);
*/
- dynamic runFromChildZone(f());
-
+ ZoneCallback bindCallback(f(), { bool runGuarded });
/**
- * Same as [runFromChildZone] but catches uncaught errors and gives them to
- * [handleUncaughtError].
+ * Equivalent to:
+ *
+ * ZoneCallback registered = registerCallback1(f);
+ * return (arg) => this.run1(registered, arg);
*/
- dynamic runFromChildZoneGuarded(f());
+ ZoneCallback1 bindCallback1(f(arg), { bool runGuarded });
/**
- * Runs [f] asynchronously in [zone].
+ * Runs [f] asynchronously.
*/
- void runAsync(void f(), _Zone zone);
+ void scheduleMicrotask(void f());
/**
* Creates a Timer where the callback is executed in this zone.
@@ -108,362 +272,312 @@ abstract class _Zone {
/**
* Creates a periodic Timer where the callback is executed in this zone.
*/
- Timer createPeriodicTimer(Duration duration, void callback(Timer timer));
+ Timer createPeriodicTimer(Duration period, 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;
+ Zone get _errorZone;
/**
- * Adds [child] as a child of `this`.
+ * Retrieves the zone-value associated with [key].
*
- * This usually means that the [child] is in the asynchronous dynamic extent
- * of `this`.
+ * If this zone does not contain the value looks up the same key in the
+ * parent zone. If the [key] is not found returns `null`.
*/
- 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);
+ operator[](Symbol key);
}
-/**
- * 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;
+class _ZoneDelegate implements ZoneDelegate {
+ final _CustomizedZone _degelationTarget;
- /// The number of children of this zone. A child's [_parentZone] is `this`.
- int _childCount = 0;
+ Zone get zone => _degelationTarget;
- /// 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;
+ const _ZoneDelegate(this._degelationTarget);
- bool _isExecutingCallback = false;
-
- _ZoneBase(this._parentZone) {
- _parentZone._addChild(this);
+ dynamic handleUncaughtError(Zone zone, e) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.handleUncaughtError == null) {
+ parent = parent.parent;
+ }
+ return (parent._description.handleUncaughtError)(
+ parent, new _ZoneDelegate(parent.parent), zone, e);
}
- _ZoneBase._defaultZone() : _parentZone = null {
- assert(this is _DefaultZone);
+ dynamic run(Zone zone, f()) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.run == null) {
+ parent = parent.parent;
Lasse Reichstein Nielsen 2013/09/23 14:24:12 If you hit the root zone here, is that also a _Cus
floitsch 2013/09/23 17:12:07 Yes. Currently the root-zone is a customized zone
+ }
+ return (parent._description.run)(
+ parent, new _ZoneDelegate(parent.parent), zone, f);
}
- _Zone get _errorZone => _parentZone._errorZone;
-
- void handleUncaughtError(error) {
- _parentZone.handleUncaughtError(error);
+ dynamic run1(Zone zone, f(arg), arg) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.run1 == null) {
+ parent = parent.parent;
+ }
+ return (parent._description.run1)(
+ parent, new _ZoneDelegate(parent.parent), zone, f, arg);
}
- bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
-
- _Zone fork() => this;
-
- expectCallback() => _openCallbacks++;
-
- cancelCallbackExpectation() {
- _openCallbacks--;
- _checkIfDone();
+ ZoneCallback registerCallback(Zone zone, f()) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.registerCallback == null) {
+ parent = parent.parent;
+ }
+ return (parent._description.registerCallback)(
+ parent, new _ZoneDelegate(parent.parent), zone, f);
}
- /**
- * 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.
- */
- void _dispose() {
- if (_parentZone != null) {
- _parentZone._removeChild(this);
+ ZoneCallback1 registerCallback1(Zone zone, f(arg)) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.registerCallback1 == null) {
+ parent = parent.parent;
}
+ return (parent._description.registerCallback1)(
+ parent, new _ZoneDelegate(parent.parent), zone, f);
}
- /**
- * 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 && _childCount == 0) {
- _dispose();
+ void scheduleMicrotask(Zone zone, f()) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.scheduleMicrotask == null) {
+ parent = parent.parent;
}
+ _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent);
+ (parent._description.scheduleMicrotask)(parent, grandParent, zone, f);
}
- void executeCallback(void f()) {
- _openCallbacks--;
- this._runUnguarded(f);
+ Timer createTimer(Zone zone, Duration duration, void f()) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.createTimer == null) {
+ parent = parent.parent;
+ }
+ return (parent._description.createTimer)(
+ parent, new _ZoneDelegate(parent.parent), zone, duration, f);
}
- void executeCallbackGuarded(void f()) {
- _openCallbacks--;
- this._runGuarded(f);
+ Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.createPeriodicTimer == null) {
+ parent = parent.parent;
+ }
+ return (parent._description.createPeriodicTimer)(
+ parent, new _ZoneDelegate(parent.parent), zone, period, f);
}
- void executePeriodicCallback(void f()) {
- this._runUnguarded(f);
+ Zone fork(Zone zone, Map<Symbol, dynamic> zoneValues,
+ ZoneDescription description) {
+ _CustomizedZone parent = _degelationTarget;
+ while (parent._description.fork == null) {
+ parent = parent.parent;
+ }
+ _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent);
+ return (parent._description.fork)(
+ parent, grandParent, zone, zoneValues, description);
}
+}
- void executePeriodicCallbackGuarded(void f()) {
- this._runGuarded(f);
+
+/**
+ * Default implementation of a [Zone].
+ */
+class _CustomizedZone implements Zone {
+ /// The parent zone.
+ final _CustomizedZone parent;
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Again, are all zones '_CustomizedZone's?
floitsch 2013/09/23 17:12:07 Currently yes.
+ /// The zone's handlers.
+ final ZoneDescription _description;
+ /// The zone's value map.
+ final Map<Symbol, dynamic> _map;
+
+ const _CustomizedZone(this.parent, this._description, this._map);
+
+ Zone get _errorZone {
+ if (_description.handleUncaughtError != null) return this;
+ return parent._errorZone;
}
- dynamic runFromChildZone(f()) => this._runUnguarded(f);
- dynamic runFromChildZoneGuarded(f()) => this._runGuarded(f);
+ bool inSameErrorZone(Zone otherZone) => _errorZone == otherZone._errorZone;
- dynamic _runInZone(f(), bool handleUncaught) {
- if (identical(_Zone._current, this)
- && !handleUncaught
- && _isExecutingCallback) {
- // No need to go through a try/catch.
- return f();
+ dynamic runGuarded(f()) {
+ try {
+ return run(f);
+ } catch (e, s) {
+ return handleUncaughtError(_asyncError(e, s));
}
+ }
- _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.
+ dynamic runGuarded1(f(arg), arg) {
try {
- try {
- return f();
- } catch(e, s) {
- if (handleUncaught) {
- handleUncaughtError(_asyncError(e, s));
- } else {
- rethrow;
- }
- }
- } finally {
- _isExecutingCallback = oldIsExecuting;
- _Zone._current = oldZone;
- _checkIfDone();
+ return run1(f, arg);
+ } catch (e, s) {
+ return handleUncaughtError(_asyncError(e, s));
}
}
- /**
- * Runs the function and catches uncaught errors.
- *
- * Uncaught errors are given to [handleUncaughtError].
- */
- dynamic _runGuarded(void f()) {
- return _runInZone(f, true);
+ ZoneCallback bindCallback(f(), { bool runGuarded }) {
+ ZoneCallback registered = registerCallback(f);
+ if (runGuarded) {
+ return () => this.runGuarded(registered);
+ } else {
+ return () => this.run(registered);
+ }
}
- /**
- * Runs the function but doesn't catch uncaught errors.
- */
- dynamic _runUnguarded(void f()) {
- return _runInZone(f, false);
+ ZoneCallback1 bindCallback1(f(arg), { bool runGuarded }) {
+ ZoneCallback1 registered = registerCallback1(f);
+ if (runGuarded) {
+ return (arg) => this.runGuarded1(registered, arg);
+ } else {
+ return (arg) => this.run1(registered, arg);
+ }
}
- void runAsync(void f(), _Zone zone) => _parentZone.runAsync(f, zone);
-
- // TODO(floitsch): the zone should just forward to the parent zone. The
- // default zone should then create the _ZoneTimer.
- Timer createTimer(Duration duration, void callback()) {
- return new _ZoneTimer(this, duration, callback);
+ operator [](Symbol key) {
+ var result = _map[key];
+ if (result != null || _map.containsKey(key)) return result;
+ // If we are not the root zone look up in the parent zone.
+ if (parent != null) return parent[key];
+ assert(this == Zone.ROOT);
+ return null;
}
- // TODO(floitsch): the zone should just forward to the parent zone. The
- // default zone should then create the _ZoneTimer.
- Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
- return new _PeriodicZoneTimer(this, duration, callback);
- }
+ // Methods that can be customized by the zone descriptions.
- void _addChild(_Zone child) {
- _childCount++;
+ dynamic handleUncaughtError(error) {
+ return new _ZoneDelegate(this).handleUncaughtError(this, error);
}
- void _removeChild(_Zone child) {
- assert(_childCount != 0);
- _childCount--;
- _checkIfDone();
+ Zone fork([Map zoneValues, ZoneDescription description]) {
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Is there a reason for putting zoneValues first. I'
floitsch 2013/09/23 17:12:07 Made them named.
+ return new _ZoneDelegate(this).fork(this, zoneValues, description);
}
-}
-/**
- * The default-zone that conceptually surrounds the `main` function.
- */
-class _DefaultZone extends _ZoneBase {
- _DefaultZone() : super._defaultZone();
-
- _Zone get _errorZone => this;
-
- void 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;
- });
+ dynamic run(f()) {
+ return new _ZoneDelegate(this).run(this, f);
}
- void runAsync(void f(), _Zone zone) {
- if (identical(this, zone)) {
- // No need to go through the zone when it's the default zone anyways.
- _scheduleAsyncCallback(f);
- return;
- }
- zone.expectCallback();
- _scheduleAsyncCallback(() {
- zone.executeCallbackGuarded(f);
- });
+ dynamic run1(f(arg), arg) {
+ return new _ZoneDelegate(this).run1(this, f, arg);
}
-}
-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.
- *
- * Executes the [_onDone] callback when the zone is done.
- */
- dynamic runWaitForCompletion(void f()) {
- return this._runUnguarded(f);
+ ZoneCallback registerCallback(f()) {
+ return new _ZoneDelegate(this).registerCallback(this, f);
}
- void _dispose() {
- super._dispose();
- _onDone();
+ ZoneCallback1 registerCallback1(f(arg)) {
+ return new _ZoneDelegate(this).registerCallback1(this, f);
}
- 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;
-
- void handleUncaughtError(error) {
- try {
- _handleError(error);
- } catch(e, s) {
- if (identical(e, error)) {
- _parentZone.handleUncaughtError(error);
- } else {
- _parentZone.handleUncaughtError(_asyncError(e, s));
- }
- }
+ void scheduleMicrotask(void f()) {
+ new _ZoneDelegate(this).scheduleMicrotask(this, f);
}
- /**
- * Runs the given function asynchronously. Executes the [_onDone] callback
- * when the zone is done.
- */
- dynamic runWaitForCompletion(void f()) {
- return this._runGuarded(f);
+ Timer createTimer(Duration duration, void f()) {
+ return new _ZoneDelegate(this).createTimer(this, duration, f);
}
- String toString() => "CatchErrors ${super.toString()}";
+ Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
+ return new _ZoneDelegate(this).createPeriodicTimer(this, duration, f);
+ }
}
-typedef void _RunAsyncInterceptor(void callback());
-
-class _RunAsyncZone extends _ZoneBase {
- final _RunAsyncInterceptor _runAsyncInterceptor;
+void _rootHandleUncaughtError(
+ Zone self, ZoneDelegate parent, Zone zone, error) {
+ _scheduleAsyncCallback(() {
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Not scheduleMicrotask?
floitsch 2013/09/23 17:12:07 This is the internals. We can change it in another
+ print("Uncaught Error: ${error}");
Lasse Reichstein Nielsen 2013/09/23 14:24:12 How about creating an UncaughtAsyncError object an
floitsch 2013/09/23 17:12:07 Ok for discussion in a different CL. (this is stil
+ var trace = getAttachedStackTrace(error);
+ _attachStackTrace(error, null);
+ if (trace != null) {
+ print("Stack Trace:\n$trace\n");
+ }
+ throw error;
+ });
+}
- _RunAsyncZone(_Zone parentZone, this._runAsyncInterceptor)
- : super(parentZone);
+dynamic _rootRun(Zone self, ZoneDelegate parent, Zone zone, f()) {
+ if (Zone._current == zone) return f();
- void runAsync(void callback(), _Zone zone) {
- zone.expectCallback();
- _parentZone.runFromChildZone(() {
- _runAsyncInterceptor(() => zone.executeCallbackGuarded(callback));
- });
+ Zone old = Zone._current;
+ try {
+ Zone._current = zone;
+ return f();
+ } finally {
+ Zone._current = old;
}
}
-typedef void _TimerCallback();
-
-/**
- * A [Timer] class that takes zones into account.
- */
-class _ZoneTimer implements Timer {
- final _Zone _zone;
- final _TimerCallback _callback;
- Timer _timer;
+dynamic _rootRun1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) {
+ if (Zone._current == zone) return f(arg);
- _ZoneTimer(this._zone, Duration duration, this._callback) {
- _zone.expectCallback();
- _timer = _createTimer(duration, this._run);
+ Zone old = Zone._current;
+ try {
+ Zone._current = zone;
+ return f(arg);
+ } finally {
+ Zone._current = old;
}
+}
- void _run() {
- _zone.executeCallbackGuarded(_callback);
- }
+ZoneCallback _rootRegisterCallback(
+ Zone self, ZoneDelegate parent, Zone zone, f()) {
+ return f;
+}
- void cancel() {
- if (_timer.isActive) _zone.cancelCallbackExpectation();
- _timer.cancel();
- }
+ZoneCallback1 _rootRegisterCallback1(
+ Zone self, ZoneDelegate parent, Zone zone, f(arg)) {
+ return f;
+}
- bool get isActive => _timer.isActive;
+void _rootScheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, f()) {
+ _scheduleAsyncCallback(f);
}
-typedef void _PeriodicTimerCallback(Timer timer);
+Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
+ Duration duration, void callback()) {
+ return _createTimer(duration, callback);
+}
-/**
- * A [Timer] class for periodic callbacks that takes zones into account.
- */
-class _PeriodicZoneTimer implements Timer {
- final _Zone _zone;
- final _PeriodicTimerCallback _callback;
- Timer _timer;
+Timer _rootCreatePeriodicTimer(
+ Zone self, ZoneDelegate parent, Zone zone,
+ Duration duration, void callback(Timer timer)) {
+ return _createPeriodicTimer(duration, callback);
+}
- _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
- _zone.expectCallback();
- _timer = _createPeriodicTimer(duration, this._run);
+Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone,
+ Map<Symbol, dynamic> zoneValues, ZoneDescription description) {
Lasse Reichstein Nielsen 2013/09/23 14:24:12 BTW, could we allow anything as keys, instead of j
floitsch 2013/09/23 17:12:07 We could. Karl and I discussed different possibili
+ if (description == null) description = const ZoneDescription();
+ if (description is! _ZoneDescription) {
Lasse Reichstein Nielsen 2013/09/23 14:24:12 "else if", since that won't be the case after the
floitsch 2013/09/23 17:12:07 should still be the case, since ZoneDescription is
+ throw new ArgumentError(
+ "ZoneDescriptions must be instantiated with the provided constructor.");
+ }
+ Map<Symbol, dynamic> copiedMap = new HashMap();
+ if (zoneValues != null) {
+ zoneValues.forEach((Symbol key, value) {
+ if (key == null) {
+ throw new ArgumentError("ZoneValue key must not be null");
+ }
+ copiedMap[key] = value;
+ });
}
+ return new _CustomizedZone(zone, description, copiedMap);
+}
- void _run(Timer timer) {
- assert(identical(_timer, timer));
- _zone.executePeriodicCallbackGuarded(() { _callback(this); });
- }
+const _ROOT_DESCRIPTION = const ZoneDescription(
Lasse Reichstein Nielsen 2013/09/23 14:24:12 double-space between "=" and "const".
floitsch 2013/09/23 17:12:07 Done.
+ handleUncaughtError: _rootHandleUncaughtError,
+ run: _rootRun,
+ run1: _rootRun1,
+ registerCallback: _rootRegisterCallback,
+ registerCallback1: _rootRegisterCallback1,
+ scheduleMicrotask: _rootScheduleMicrotask,
+ createTimer: _rootCreateTimer,
+ createPeriodicTimer: _rootCreatePeriodicTimer,
+ fork: _rootFork
+);
- void cancel() {
- if (_timer.isActive) _zone.cancelCallbackExpectation();
- _timer.cancel();
- }
+const _ROOT_ZONE = const _CustomizedZone(null, _ROOT_DESCRIPTION, const {});
Lasse Reichstein Nielsen 2013/09/23 14:24:12 Make the last argument "const <Symbol,dynamic>{}"
floitsch 2013/09/23 17:12:07 Done.
- bool get isActive => _timer.isActive;
-}
/**
* Runs [body] in its own zone.
@@ -472,32 +586,6 @@ class _PeriodicZoneTimer implements Timer {
* errors, synchronous or asynchronous, in the zone are caught and handled
* by the callback.
*
- * The [onDone] handler (if non-null) is invoked when the zone has no more
- * outstanding callbacks. *Deprecated*: this method is less useful than it
- * seems, because it assumes that every registered callback is always invoked.
- * There are, however, many *valid* reasons not to complete futures or to abort
- * a future-chain. In general it is a bad idea to rely on `onDone`.
- *
- * The [onRunAsync] handler (if non-null) is invoked when the [body] executes
- * [runAsync]. The handler is invoked in the outer zone and can therefore
- * execute [runAsync] without recursing. The given callback must be
- * executed eventually. Otherwise the nested zone will not complete. It must be
- * executed only once.
- *
- * 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.
@@ -510,6 +598,54 @@ class _PeriodicZoneTimer implements Timer {
* }, onError: (e) { print("unused error handler"); });
* }, onError: (e) { print("catches error of first error-zone."); });
*
+ * Example:
+ *
+ * runZonedExperimental(() {
+ * new Future(() { throw "asynchronous error"; });
+ * }, onError: print); // Will print "asynchronous error".
+ */
+dynamic runZoned(body(),
+ { Map<Symbol, dynamic> zoneValues,
+ ZoneDescription zoneDescription,
+ void onError(error) }) {
+ HandleUncaughtErrorHandler errorHandler;
+ if (onError != null) {
+ errorHandler = (Zone self, ZoneDelegate parent, Zone zone, error) {
+ try {
+ return parent.zone.run1(onError, error);
+ } catch(e, s) {
+ if (identical(e, error)) {
+ return parent.handleUncaughtError(zone, error);
+ } else {
+ return parent.handleUncaughtError(zone, _asyncError(e, s));
+ }
+ }
+ };
+ }
+ if (zoneDescription == null) {
+ zoneDescription = new ZoneDescription(handleUncaughtError: errorHandler);
+ } else if (errorHandler != null) {
+ zoneDescription =
+ new ZoneDescription.from(zoneDescription,
+ handleUncaughtError: errorHandler);
+ }
+ Zone zone = Zone.current.fork(zoneValues, zoneDescription);
+ if (onError != null) {
+ return zone.runGuarded(body);
+ } else {
+ return zone.run(body);
+ }
+}
+
+/**
+ * Deprecated. Use `runZoned` instead or create your own [ZoneDescription].
+ *
+ * The [onRunAsync] handler (if non-null) is invoked when the [body] executes
+ * [runAsync]. The handler is invoked in the outer zone and can therefore
+ * execute [runAsync] without recursing. The given callback must be
+ * executed eventually. Otherwise the nested zone will not complete. It must be
+ * executed only once.
+ *
* The following example prints the stack trace whenever a callback is
* registered using [runAsync] (which is also used by [Completer]s and
* [StreamController]s.
@@ -519,26 +655,44 @@ class _PeriodicZoneTimer implements Timer {
* printStackTrace();
* runAsync(callback);
* });
+ *
+ * Note: the `onDone` handler is ignored.
*/
+@deprecated
runZonedExperimental(body(),
{ void onRunAsync(void callback()),
void onError(error),
void onDone() }) {
- if (onRunAsync != null) {
- _RunAsyncZone zone = new _RunAsyncZone(_Zone._current, onRunAsync);
- return zone._runUnguarded(() {
- return runZonedExperimental(body, onError: onError, onDone: onDone);
- });
+ if (onRunAsync == null) {
+ return runZoned(body, onError: onError);
}
-
- // 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);
+ HandleUncaughtErrorHandler errorHandler;
+ if (onError != null) {
+ errorHandler = (Zone self, ZoneDelegate parent, Zone zone, error) {
+ try {
+ return parent.zone.run1(onError, error);
+ } catch(e, s) {
+ if (identical(e, error)) {
+ return parent.handleUncaughtError(zone, error);
+ } else {
+ return parent.handleUncaughtError(zone, _asyncError(e, s));
+ }
+ }
+ };
+ }
+ ScheduleMicrotaskHandler asyncHandler;
+ if (onRunAsync != null) {
+ asyncHandler = (Zone self, ZoneDelegate parent, Zone zone, f()) {
+ parent.zone.run1(onRunAsync, () => zone.runGuarded(f));
+ };
+ }
+ ZoneDescription description =
+ new ZoneDescription(handleUncaughtError: errorHandler,
+ scheduleMicrotask: asyncHandler);
+ Zone zone = Zone.current.fork(null, description);
+ if (onError != null) {
+ return zone.runGuarded(body);
+ } else {
+ return zone.run(body);
}
- if (onDone == null) onDone = _nullDoneHandler;
- _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
- return zone.runWaitForCompletion(body);
}

Powered by Google App Engine
This is Rietveld 408576698