Chromium Code Reviews| 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); |
| } |