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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.async; 5 part of dart.async;
6 6
7 typedef dynamic ZoneCallback();
8 typedef dynamic ZoneCallback1(arg);
9
10 typedef dynamic HandleUncaughtErrorHandler(
11 Zone self, ZoneDelegate parent, Zone zone, e);
12 typedef dynamic RunHandler(Zone self, ZoneDelegate parent, Zone zone, f());
13 typedef dynamic Run1Handler(
14 Zone self, ZoneDelegate parent, Zone zone, f(arg), arg);
15 typedef ZoneCallback RegisterCallbackHandler(
16 Zone self, ZoneDelegate parent, Zone zone, f());
17 typedef ZoneCallback1 RegisterCallback1Handler(
18 Zone self, ZoneDelegate parent, Zone zone, f(arg));
19 typedef void ScheduleMicrotaskHandler(
20 Zone self, ZoneDelegate parent, Zone zone, f());
21 typedef Timer CreateTimerHandler(
22 Zone self, ZoneDelegate parent, Zone zone, Duration duration, void f());
23 typedef Timer CreatePeriodicTimerHandler(
24 Zone self, ZoneDelegate parent, Zone zone,
25 Duration period, void f(Timer timer));
26 typedef Zone ForkHandler(Zone self, ZoneDelegate parent, Zone zone,
27 Map<Symbol, dynamic> zoneValues,
28 ZoneDescription description);
29
30 /**
31 * 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
32 *
33 * When forking a new zone (see [Zone.fork]) one can override the default
34 * behavior of the zone by providing callbacks. These callbacks must be
35 * given in an instance of this class.
36 *
37 * Handlers have the same signature as the same-named methods on [Zone] but
38 * receive three additional arguments:
39 *
40 * 1. the zone the handlers are attached to (the "self" zone).
41 * 2. a [ZoneDelegate] to the parent zone.
42 * 3. the zone that first received the request (before the request was
43 * 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.
44 *
45 * 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.
46 * parent handler), or forward to the parent zone, potentially modifying the
47 * arguments on the way.
48 */
49 abstract class ZoneDescription {
50 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.
51 void handleUncaughtError(
52 Zone self, ZoneDelegate parent, Zone zone, e): null,
53 dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()): null,
54 dynamic run1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg): null,
55 ZoneCallback registerCallback(
56 Zone self, ZoneDelegate parent, Zone zone, f()): null,
57 ZoneCallback1 registerCallback1(
58 Zone self, ZoneDelegate parent, Zone zone, f(arg)): null,
59 void scheduleMicrotask(
60 Zone self, ZoneDelegate parent, Zone zone, f()): null,
61 Timer createTimer(Zone self, ZoneDelegate parent, Zone zone,
62 Duration duration, void f()): null,
63 Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
64 Duration period, void f(Timer timer)): null,
65 Zone fork(Zone self, ZoneDelegate parent, Zone zone,
66 Map zoneValues, ZoneDescription description): null
67 }) = _ZoneDescription;
68
69 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.
70 void handleUncaughtError(
71 Zone self, ZoneDelegate parent, Zone zone, e): null,
72 dynamic run(Zone self, ZoneDelegate parent, Zone zone, f()): null,
73 dynamic run1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg): null,
74 ZoneCallback registerCallback(
75 Zone self, ZoneDelegate parent, Zone zone, f()): null,
76 ZoneCallback1 registerCallback1(
77 Zone self, ZoneDelegate parent, Zone zone, f(arg)): null,
78 void scheduleMicrotask(
79 Zone self, ZoneDelegate parent, Zone zone, f()): null,
80 Timer createTimer(Zone self, ZoneDelegate parent, Zone zone,
81 Duration duration, void f()): null,
82 Timer createPeriodicTimer(Zone self, ZoneDelegate parent, Zone zone,
83 Duration period, void f(Timer timer)): null,
84 Zone fork(Zone self, ZoneDelegate parent, Zone zone,
85 Map<Symbol, dynamic> zoneValues,
86 ZoneDescription description): null
87 }) {
88 return new ZoneDescription(
89 handleUncaughtError: handleUncaughtError != null
90 ? handleUncaughtError
91 : other.handleUncaughtError,
92 run: run != null ? run : other.run,
93 run1: run1 != null ? run1 : other.run1,
94 registerCallback: registerCallback != null
95 ? registerCallback
96 : other.registerCallback,
97 registerCallback1: registerCallback1 != null
98 ? registerCallback1
99 : other.registerCallback1,
100 scheduleMicrotask: scheduleMicrotask != null
101 ? scheduleMicrotask
102 : other.scheduleMicrotask,
103 createTimer : createTimer != null ? createTimer : other.createTimer,
104 createPeriodicTimer: createPeriodicTimer != null
105 ? createPeriodicTimer
106 : other.createPeriodicTimer,
107 fork: fork != null ? fork : other.fork);
108 }
109
110 HandleUncaughtErrorHandler get handleUncaughtError;
111 RunHandler get run;
112 Run1Handler get run1;
113 RegisterCallbackHandler get registerCallback;
114 RegisterCallback1Handler get registerCallback1;
115 ScheduleMicrotaskHandler get scheduleMicrotask;
116 CreateTimerHandler get createTimer;
117 CreatePeriodicTimerHandler get createPeriodicTimer;
118 ForkHandler get fork;
119 }
120
121 /**
122 * Internal [ZoneDescription] class.
123 *
124 * The implementation wants to rely on the fact that the getters cannot change
125 * dynamically. We thus require users to go through the redirecting
126 * [ZoneDescription] constructor which instantiates this class.
127 */
128 class _ZoneDescription implements ZoneDescription {
129 const _ZoneDescription({
130 this.handleUncaughtError: null,
131 this.run: null,
132 this.run1: null,
133 this.registerCallback: null,
134 this.registerCallback1: null,
135 this.scheduleMicrotask: null,
136 this.createTimer: null,
137 this.createPeriodicTimer: null,
138 this.fork: null
139 });
140
141 // TODO(13406): Enable types when dart2js supports it.
142 final /*HandleUncaughtErrorHandler*/ handleUncaughtError;
143 final /*RunHandler*/ run;
144 final /*Run1Handler*/ run1;
145 final /*RegisterCallbackHandler*/ registerCallback;
146 final /*RegisterCallback1Handler*/ registerCallback1;
147 final /*ScheduleMicrotaskHandler*/ scheduleMicrotask;
148 final /*CreateTimerHandler*/ createTimer;
149 final /*CreatePeriodicTimerHandler*/ createPeriodicTimer;
150 final /*ForkHandler*/ fork;
151 }
152
153 /**
154 * 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".
155 *
156 * When forwarding to parent zones one can't just invoke the parent zone's
157 * exposed functions (like [Zone.run]), but one needs to provide more
158 * information (like the zone the `run` was initiated). Zone callbacks thus
159 * receive more information including this [ZoneDelegate] class. When delegating
160 * to the parent zone one should go through the given instance instead of
161 * directly invoking the parent zone.
162 */
163 abstract class ZoneDelegate {
164 /// The [Zone] this class wraps.
165 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
166
167 dynamic handleUncaughtError(Zone zone, e);
168 dynamic run(Zone zone, f());
169 dynamic run1(Zone zone, f(arg), arg);
170 ZoneCallback registerCallback(Zone zone, f());
171 ZoneCallback1 registerCallback1(Zone zone, f(arg));
172 void scheduleMicrotask(Zone zone, f());
173 Timer createTimer(Zone zone, Duration duration, void f());
174 Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer));
175 Zone fork(Zone zone, Map zoneValues, ZoneDescription description);
176 }
177
7 /** 178 /**
8 * A Zone represents the asynchronous version of a dynamic extent. Asynchronous 179 * A Zone represents the asynchronous version of a dynamic extent. Asynchronous
9 * callbacks are executed in the zone they have been queued in. For example, 180 * callbacks are executed in the zone they have been queued in. For example,
10 * the callback of a `future.then` is executed in the same zone as the one where 181 * the callback of a `future.then` is executed in the same zone as the one where
11 * the `then` was invoked. 182 * the `then` was invoked.
12 */ 183 */
13 abstract class _Zone { 184 abstract class Zone {
185 // Private constructor so that it is not possible instantiate a Zone class.
186 Zone._();
187
188 /// The root zone that is implicitly created.
189 static const Zone ROOT = _ROOT_ZONE;
190
14 /// The currently running zone. 191 /// The currently running zone.
15 static _Zone _current = new _DefaultZone(); 192 static Zone _current = _ROOT_ZONE;
16 193
17 static _Zone get current => _current; 194 static Zone get current => _current;
18 195
19 void handleUncaughtError(error); 196 dynamic handleUncaughtError(error);
197
198 /**
199 * Returns the parent zone.
200 *
201 * Returns `null` if `this` is the [ROOT] zone.
202 */
203 Zone get parent;
20 204
21 /** 205 /**
22 * Returns true if `this` and [otherZone] are in the same error zone. 206 * Returns true if `this` and [otherZone] are in the same error zone.
23 */ 207 *
24 bool inSameErrorZone(_Zone otherZone); 208 * Two zones are in the same error zone if they share the same
25 209 * [handleUncaughtError] callback.
26 /** 210 */
27 * Returns a zone for reentry in the zone. 211 bool inSameErrorZone(Zone otherZone);
28 * 212
29 * The returned zone is equivalent to `this` (and frequently is indeed 213 /**
30 * `this`). 214 * Creates a new zone as a child of `this`.
31 * 215 */
32 * The main purpose of this method is to allow `this` to attach debugging 216 Zone fork([Map<Symbol, dynamic> zoneValues, ZoneDescription description]);
33 * information to the returned zone. 217
34 */ 218 /**
35 _Zone fork(); 219 * Executes the given function [f] in this zone.
36 220 */
37 /** 221 dynamic run(f());
38 * Tells the zone that it needs to wait for one more callback before it is 222
39 * done. 223 /**
40 * 224 * Executes the given callback [f] with argument [arg] in this zone.
41 * Use [executeCallback] or [cancelCallbackExpectation] when the callback is 225 */
42 * executed (or canceled). 226 dynamic run1(f(arg), var arg);
43 */ 227
44 void expectCallback(); 228 /**
45 229 * Executes the given function [f] in this zone.
46 /** 230 *
47 * Tells the zone not to wait for a callback anymore. 231 * Same as [run] but catches uncaught errors and gives them to
48 * 232 * [handleUncaughtError].
49 * Prefer calling [executeCallback], instead. This method is mostly useful 233 */
50 * for repeated callbacks (for example with [Timer.periodic]). In this case 234 dynamic runGuarded(f());
51 * one should should call [expectCallback] when the repeated callback is
52 * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled.
53 */
54 void cancelCallbackExpectation();
55 235
56 /** 236 /**
57 * Executes the given callback [f] in this zone. 237 * Executes the given callback [f] in this zone.
58 * 238 *
59 * Decrements the number of callbacks this zone is waiting for (see 239 * Same as [run1] but catches uncaught errors and gives them to
60 * [expectCallback]).
61 */
62 void executeCallback(void f());
63
64 /**
65 * Same as [executeCallback] but catches uncaught errors and gives them to
66 * [handleUncaughtError]. 240 * [handleUncaughtError].
67 */ 241 */
68 void executeCallbackGuarded(void f()); 242 dynamic runGuarded1(f(arg), var arg);
69 243
70 /** 244 ZoneCallback registerCallback(f());
71 * Same as [executeCallback] but does not decrement the number of 245 ZoneCallback1 registerCallback1(f(arg));
72 * callbacks this zone is waiting for (see [expectCallback]). 246
73 */ 247 /**
74 void executePeriodicCallback(void f()); 248 * Equivalent to:
75 249 *
76 /** 250 * ZoneCallback registered = registerCallback(f);
77 * Same as [executePeriodicCallback] but catches uncaught errors and gives 251 * return () => this.run(registered);
78 * them to [handleUncaughtError]. 252 */
79 */ 253 ZoneCallback bindCallback(f(), { bool runGuarded });
80 void executePeriodicCallbackGuarded(void f()); 254 /**
81 255 * Equivalent to:
82 /** 256 *
83 * Executes [f] in `this` zone. 257 * ZoneCallback registered = registerCallback1(f);
84 * 258 * return (arg) => this.run1(registered, arg);
85 * The behavior of this method should be the same as 259 */
86 * [executePeriodicCallback] except that it can have a return value. 260 ZoneCallback1 bindCallback1(f(arg), { bool runGuarded });
87 * 261
88 * Returns the result of the invocation. 262 /**
89 */ 263 * Runs [f] asynchronously.
90 dynamic runFromChildZone(f()); 264 */
91 265 void scheduleMicrotask(void f());
92 /**
93 * Same as [runFromChildZone] but catches uncaught errors and gives them to
94 * [handleUncaughtError].
95 */
96 dynamic runFromChildZoneGuarded(f());
97
98 /**
99 * Runs [f] asynchronously in [zone].
100 */
101 void runAsync(void f(), _Zone zone);
102 266
103 /** 267 /**
104 * Creates a Timer where the callback is executed in this zone. 268 * Creates a Timer where the callback is executed in this zone.
105 */ 269 */
106 Timer createTimer(Duration duration, void callback()); 270 Timer createTimer(Duration duration, void callback());
107 271
108 /** 272 /**
109 * Creates a periodic Timer where the callback is executed in this zone. 273 * Creates a periodic Timer where the callback is executed in this zone.
110 */ 274 */
111 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)); 275 Timer createPeriodicTimer(Duration period, void callback(Timer timer));
112 276
113 /** 277 /**
114 * The error zone is the one that is responsible for dealing with uncaught 278 * The error zone is the one that is responsible for dealing with uncaught
115 * errors. Errors are not allowed to cross zones with different error-zones. 279 * errors. Errors are not allowed to cross zones with different error-zones.
116 */ 280 */
117 _Zone get _errorZone; 281 Zone get _errorZone;
118 282
119 /** 283 /**
120 * Adds [child] as a child of `this`. 284 * Retrieves the zone-value associated with [key].
121 * 285 *
122 * This usually means that the [child] is in the asynchronous dynamic extent 286 * If this zone does not contain the value looks up the same key in the
123 * of `this`. 287 * parent zone. If the [key] is not found returns `null`.
124 */ 288 */
125 void _addChild(_Zone child); 289 operator[](Symbol key);
126 290 }
127 /** 291
128 * Removes [child] from `this`' children. 292 class _ZoneDelegate implements ZoneDelegate {
129 * 293 final _CustomizedZone _degelationTarget;
130 * This usually means that the [child] has finished executing and is done. 294
131 */ 295 Zone get zone => _degelationTarget;
132 void _removeChild(_Zone child); 296
133 } 297 const _ZoneDelegate(this._degelationTarget);
298
299 dynamic handleUncaughtError(Zone zone, e) {
300 _CustomizedZone parent = _degelationTarget;
301 while (parent._description.handleUncaughtError == null) {
302 parent = parent.parent;
303 }
304 return (parent._description.handleUncaughtError)(
305 parent, new _ZoneDelegate(parent.parent), zone, e);
306 }
307
308 dynamic run(Zone zone, f()) {
309 _CustomizedZone parent = _degelationTarget;
310 while (parent._description.run == null) {
311 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
312 }
313 return (parent._description.run)(
314 parent, new _ZoneDelegate(parent.parent), zone, f);
315 }
316
317 dynamic run1(Zone zone, f(arg), arg) {
318 _CustomizedZone parent = _degelationTarget;
319 while (parent._description.run1 == null) {
320 parent = parent.parent;
321 }
322 return (parent._description.run1)(
323 parent, new _ZoneDelegate(parent.parent), zone, f, arg);
324 }
325
326 ZoneCallback registerCallback(Zone zone, f()) {
327 _CustomizedZone parent = _degelationTarget;
328 while (parent._description.registerCallback == null) {
329 parent = parent.parent;
330 }
331 return (parent._description.registerCallback)(
332 parent, new _ZoneDelegate(parent.parent), zone, f);
333 }
334
335 ZoneCallback1 registerCallback1(Zone zone, f(arg)) {
336 _CustomizedZone parent = _degelationTarget;
337 while (parent._description.registerCallback1 == null) {
338 parent = parent.parent;
339 }
340 return (parent._description.registerCallback1)(
341 parent, new _ZoneDelegate(parent.parent), zone, f);
342 }
343
344 void scheduleMicrotask(Zone zone, f()) {
345 _CustomizedZone parent = _degelationTarget;
346 while (parent._description.scheduleMicrotask == null) {
347 parent = parent.parent;
348 }
349 _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent);
350 (parent._description.scheduleMicrotask)(parent, grandParent, zone, f);
351 }
352
353 Timer createTimer(Zone zone, Duration duration, void f()) {
354 _CustomizedZone parent = _degelationTarget;
355 while (parent._description.createTimer == null) {
356 parent = parent.parent;
357 }
358 return (parent._description.createTimer)(
359 parent, new _ZoneDelegate(parent.parent), zone, duration, f);
360 }
361
362 Timer createPeriodicTimer(Zone zone, Duration period, void f(Timer timer)) {
363 _CustomizedZone parent = _degelationTarget;
364 while (parent._description.createPeriodicTimer == null) {
365 parent = parent.parent;
366 }
367 return (parent._description.createPeriodicTimer)(
368 parent, new _ZoneDelegate(parent.parent), zone, period, f);
369 }
370
371 Zone fork(Zone zone, Map<Symbol, dynamic> zoneValues,
372 ZoneDescription description) {
373 _CustomizedZone parent = _degelationTarget;
374 while (parent._description.fork == null) {
375 parent = parent.parent;
376 }
377 _ZoneDelegate grandParent = new _ZoneDelegate(parent.parent);
378 return (parent._description.fork)(
379 parent, grandParent, zone, zoneValues, description);
380 }
381 }
382
134 383
135 /** 384 /**
136 * Basic implementation of a [_Zone]. This class is intended for subclassing. 385 * Default implementation of a [Zone].
137 */ 386 */
138 class _ZoneBase implements _Zone { 387 class _CustomizedZone implements Zone {
139 /// The parent zone. [null] if `this` is the default zone. 388 /// The parent zone.
140 final _Zone _parentZone; 389 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.
141 390 /// The zone's handlers.
142 /// The number of children of this zone. A child's [_parentZone] is `this`. 391 final ZoneDescription _description;
143 int _childCount = 0; 392 /// The zone's value map.
144 393 final Map<Symbol, dynamic> _map;
145 /// The number of outstanding (asynchronous) callbacks. As long as the 394
146 /// number is greater than 0 it means that the zone is not done yet. 395 const _CustomizedZone(this.parent, this._description, this._map);
147 int _openCallbacks = 0; 396
148 397 Zone get _errorZone {
149 bool _isExecutingCallback = false; 398 if (_description.handleUncaughtError != null) return this;
150 399 return parent._errorZone;
151 _ZoneBase(this._parentZone) { 400 }
152 _parentZone._addChild(this); 401
153 } 402 bool inSameErrorZone(Zone otherZone) => _errorZone == otherZone._errorZone;
154 403
155 _ZoneBase._defaultZone() : _parentZone = null { 404 dynamic runGuarded(f()) {
156 assert(this is _DefaultZone);
157 }
158
159 _Zone get _errorZone => _parentZone._errorZone;
160
161 void handleUncaughtError(error) {
162 _parentZone.handleUncaughtError(error);
163 }
164
165 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
166
167 _Zone fork() => this;
168
169 expectCallback() => _openCallbacks++;
170
171 cancelCallbackExpectation() {
172 _openCallbacks--;
173 _checkIfDone();
174 }
175
176 /**
177 * Cleans up this zone when it is done.
178 *
179 * This releases internal memore structures that are no longer necessary.
180 *
181 * A zone is done when its dynamic extent has finished executing and
182 * there are no outstanding asynchronous callbacks.
183 */
184 void _dispose() {
185 if (_parentZone != null) {
186 _parentZone._removeChild(this);
187 }
188 }
189
190 /**
191 * Checks if the zone is done and doesn't have any outstanding callbacks
192 * anymore.
193 *
194 * This method is called when an operation has decremented the
195 * outstanding-callback count, or when a child has been removed.
196 */
197 void _checkIfDone() {
198 if (!_isExecutingCallback && _openCallbacks == 0 && _childCount == 0) {
199 _dispose();
200 }
201 }
202
203 void executeCallback(void f()) {
204 _openCallbacks--;
205 this._runUnguarded(f);
206 }
207
208 void executeCallbackGuarded(void f()) {
209 _openCallbacks--;
210 this._runGuarded(f);
211 }
212
213 void executePeriodicCallback(void f()) {
214 this._runUnguarded(f);
215 }
216
217 void executePeriodicCallbackGuarded(void f()) {
218 this._runGuarded(f);
219 }
220
221 dynamic runFromChildZone(f()) => this._runUnguarded(f);
222 dynamic runFromChildZoneGuarded(f()) => this._runGuarded(f);
223
224 dynamic _runInZone(f(), bool handleUncaught) {
225 if (identical(_Zone._current, this)
226 && !handleUncaught
227 && _isExecutingCallback) {
228 // No need to go through a try/catch.
229 return f();
230 }
231
232 _Zone oldZone = _Zone._current;
233 _Zone._current = this;
234 // While we are executing the function we don't want to have other
235 // synchronous calls to think that they closed the zone. By incrementing
236 // the _openCallbacks count we make sure that their test will fail.
237 // As a side effect it will make nested calls faster since they are
238 // (probably) in the same zone and have an _openCallbacks > 0.
239 bool oldIsExecuting = _isExecutingCallback;
240 _isExecutingCallback = true;
241 // TODO(430): remove second try when VM bug is fixed.
242 try { 405 try {
243 try { 406 return run(f);
244 return f(); 407 } catch (e, s) {
245 } catch(e, s) { 408 return handleUncaughtError(_asyncError(e, s));
246 if (handleUncaught) { 409 }
247 handleUncaughtError(_asyncError(e, s)); 410 }
248 } else { 411
249 rethrow; 412 dynamic runGuarded1(f(arg), arg) {
250 } 413 try {
414 return run1(f, arg);
415 } catch (e, s) {
416 return handleUncaughtError(_asyncError(e, s));
417 }
418 }
419
420 ZoneCallback bindCallback(f(), { bool runGuarded }) {
421 ZoneCallback registered = registerCallback(f);
422 if (runGuarded) {
423 return () => this.runGuarded(registered);
424 } else {
425 return () => this.run(registered);
426 }
427 }
428
429 ZoneCallback1 bindCallback1(f(arg), { bool runGuarded }) {
430 ZoneCallback1 registered = registerCallback1(f);
431 if (runGuarded) {
432 return (arg) => this.runGuarded1(registered, arg);
433 } else {
434 return (arg) => this.run1(registered, arg);
435 }
436 }
437
438 operator [](Symbol key) {
439 var result = _map[key];
440 if (result != null || _map.containsKey(key)) return result;
441 // If we are not the root zone look up in the parent zone.
442 if (parent != null) return parent[key];
443 assert(this == Zone.ROOT);
444 return null;
445 }
446
447 // Methods that can be customized by the zone descriptions.
448
449 dynamic handleUncaughtError(error) {
450 return new _ZoneDelegate(this).handleUncaughtError(this, error);
451 }
452
453 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.
454 return new _ZoneDelegate(this).fork(this, zoneValues, description);
455 }
456
457 dynamic run(f()) {
458 return new _ZoneDelegate(this).run(this, f);
459 }
460
461 dynamic run1(f(arg), arg) {
462 return new _ZoneDelegate(this).run1(this, f, arg);
463 }
464
465 ZoneCallback registerCallback(f()) {
466 return new _ZoneDelegate(this).registerCallback(this, f);
467 }
468
469 ZoneCallback1 registerCallback1(f(arg)) {
470 return new _ZoneDelegate(this).registerCallback1(this, f);
471 }
472
473 void scheduleMicrotask(void f()) {
474 new _ZoneDelegate(this).scheduleMicrotask(this, f);
475 }
476
477 Timer createTimer(Duration duration, void f()) {
478 return new _ZoneDelegate(this).createTimer(this, duration, f);
479 }
480
481 Timer createPeriodicTimer(Duration duration, void f(Timer timer)) {
482 return new _ZoneDelegate(this).createPeriodicTimer(this, duration, f);
483 }
484 }
485
486 void _rootHandleUncaughtError(
487 Zone self, ZoneDelegate parent, Zone zone, error) {
488 _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
489 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
490 var trace = getAttachedStackTrace(error);
491 _attachStackTrace(error, null);
492 if (trace != null) {
493 print("Stack Trace:\n$trace\n");
494 }
495 throw error;
496 });
497 }
498
499 dynamic _rootRun(Zone self, ZoneDelegate parent, Zone zone, f()) {
500 if (Zone._current == zone) return f();
501
502 Zone old = Zone._current;
503 try {
504 Zone._current = zone;
505 return f();
506 } finally {
507 Zone._current = old;
508 }
509 }
510
511 dynamic _rootRun1(Zone self, ZoneDelegate parent, Zone zone, f(arg), arg) {
512 if (Zone._current == zone) return f(arg);
513
514 Zone old = Zone._current;
515 try {
516 Zone._current = zone;
517 return f(arg);
518 } finally {
519 Zone._current = old;
520 }
521 }
522
523 ZoneCallback _rootRegisterCallback(
524 Zone self, ZoneDelegate parent, Zone zone, f()) {
525 return f;
526 }
527
528 ZoneCallback1 _rootRegisterCallback1(
529 Zone self, ZoneDelegate parent, Zone zone, f(arg)) {
530 return f;
531 }
532
533 void _rootScheduleMicrotask(Zone self, ZoneDelegate parent, Zone zone, f()) {
534 _scheduleAsyncCallback(f);
535 }
536
537 Timer _rootCreateTimer(Zone self, ZoneDelegate parent, Zone zone,
538 Duration duration, void callback()) {
539 return _createTimer(duration, callback);
540 }
541
542 Timer _rootCreatePeriodicTimer(
543 Zone self, ZoneDelegate parent, Zone zone,
544 Duration duration, void callback(Timer timer)) {
545 return _createPeriodicTimer(duration, callback);
546 }
547
548 Zone _rootFork(Zone self, ZoneDelegate parent, Zone zone,
549 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
550 if (description == null) description = const ZoneDescription();
551 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
552 throw new ArgumentError(
553 "ZoneDescriptions must be instantiated with the provided constructor.");
554 }
555 Map<Symbol, dynamic> copiedMap = new HashMap();
556 if (zoneValues != null) {
557 zoneValues.forEach((Symbol key, value) {
558 if (key == null) {
559 throw new ArgumentError("ZoneValue key must not be null");
251 } 560 }
252 } finally { 561 copiedMap[key] = value;
253 _isExecutingCallback = oldIsExecuting;
254 _Zone._current = oldZone;
255 _checkIfDone();
256 }
257 }
258
259 /**
260 * Runs the function and catches uncaught errors.
261 *
262 * Uncaught errors are given to [handleUncaughtError].
263 */
264 dynamic _runGuarded(void f()) {
265 return _runInZone(f, true);
266 }
267
268 /**
269 * Runs the function but doesn't catch uncaught errors.
270 */
271 dynamic _runUnguarded(void f()) {
272 return _runInZone(f, false);
273 }
274
275 void runAsync(void f(), _Zone zone) => _parentZone.runAsync(f, zone);
276
277 // TODO(floitsch): the zone should just forward to the parent zone. The
278 // default zone should then create the _ZoneTimer.
279 Timer createTimer(Duration duration, void callback()) {
280 return new _ZoneTimer(this, duration, callback);
281 }
282
283 // TODO(floitsch): the zone should just forward to the parent zone. The
284 // default zone should then create the _ZoneTimer.
285 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
286 return new _PeriodicZoneTimer(this, duration, callback);
287 }
288
289 void _addChild(_Zone child) {
290 _childCount++;
291 }
292
293 void _removeChild(_Zone child) {
294 assert(_childCount != 0);
295 _childCount--;
296 _checkIfDone();
297 }
298 }
299
300 /**
301 * The default-zone that conceptually surrounds the `main` function.
302 */
303 class _DefaultZone extends _ZoneBase {
304 _DefaultZone() : super._defaultZone();
305
306 _Zone get _errorZone => this;
307
308 void handleUncaughtError(error) {
309 _scheduleAsyncCallback(() {
310 print("Uncaught Error: ${error}");
311 var trace = getAttachedStackTrace(error);
312 _attachStackTrace(error, null);
313 if (trace != null) {
314 print("Stack Trace:\n$trace\n");
315 }
316 throw error;
317 }); 562 });
318 } 563 }
319 564 return new _CustomizedZone(zone, description, copiedMap);
320 void runAsync(void f(), _Zone zone) { 565 }
321 if (identical(this, zone)) { 566
322 // No need to go through the zone when it's the default zone anyways. 567 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.
323 _scheduleAsyncCallback(f); 568 handleUncaughtError: _rootHandleUncaughtError,
324 return; 569 run: _rootRun,
325 } 570 run1: _rootRun1,
326 zone.expectCallback(); 571 registerCallback: _rootRegisterCallback,
327 _scheduleAsyncCallback(() { 572 registerCallback1: _rootRegisterCallback1,
328 zone.executeCallbackGuarded(f); 573 scheduleMicrotask: _rootScheduleMicrotask,
329 }); 574 createTimer: _rootCreateTimer,
330 } 575 createPeriodicTimer: _rootCreatePeriodicTimer,
331 } 576 fork: _rootFork
332 577 );
333 typedef void _CompletionCallback(); 578
334 579 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.
335 /** 580
336 * A zone that executes a callback when the zone is dead.
337 */
338 class _WaitForCompletionZone extends _ZoneBase {
339 final _CompletionCallback _onDone;
340
341 _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
342
343 /**
344 * Runs the given function.
345 *
346 * Executes the [_onDone] callback when the zone is done.
347 */
348 dynamic runWaitForCompletion(void f()) {
349 return this._runUnguarded(f);
350 }
351
352 void _dispose() {
353 super._dispose();
354 _onDone();
355 }
356
357 String toString() => "WaitForCompletion ${super.toString()}";
358 }
359
360 typedef void _HandleErrorCallback(error);
361
362 /**
363 * A zone that collects all uncaught errors and provides them in a stream.
364 * The stream is closed when the zone is done.
365 */
366 class _CatchErrorsZone extends _WaitForCompletionZone {
367 final _HandleErrorCallback _handleError;
368
369 _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone())
370 : super(parentZone, onDone);
371
372 _Zone get _errorZone => this;
373
374 void handleUncaughtError(error) {
375 try {
376 _handleError(error);
377 } catch(e, s) {
378 if (identical(e, error)) {
379 _parentZone.handleUncaughtError(error);
380 } else {
381 _parentZone.handleUncaughtError(_asyncError(e, s));
382 }
383 }
384 }
385
386 /**
387 * Runs the given function asynchronously. Executes the [_onDone] callback
388 * when the zone is done.
389 */
390 dynamic runWaitForCompletion(void f()) {
391 return this._runGuarded(f);
392 }
393
394 String toString() => "CatchErrors ${super.toString()}";
395 }
396
397 typedef void _RunAsyncInterceptor(void callback());
398
399 class _RunAsyncZone extends _ZoneBase {
400 final _RunAsyncInterceptor _runAsyncInterceptor;
401
402 _RunAsyncZone(_Zone parentZone, this._runAsyncInterceptor)
403 : super(parentZone);
404
405 void runAsync(void callback(), _Zone zone) {
406 zone.expectCallback();
407 _parentZone.runFromChildZone(() {
408 _runAsyncInterceptor(() => zone.executeCallbackGuarded(callback));
409 });
410 }
411 }
412
413 typedef void _TimerCallback();
414
415 /**
416 * A [Timer] class that takes zones into account.
417 */
418 class _ZoneTimer implements Timer {
419 final _Zone _zone;
420 final _TimerCallback _callback;
421 Timer _timer;
422
423 _ZoneTimer(this._zone, Duration duration, this._callback) {
424 _zone.expectCallback();
425 _timer = _createTimer(duration, this._run);
426 }
427
428 void _run() {
429 _zone.executeCallbackGuarded(_callback);
430 }
431
432 void cancel() {
433 if (_timer.isActive) _zone.cancelCallbackExpectation();
434 _timer.cancel();
435 }
436
437 bool get isActive => _timer.isActive;
438 }
439
440 typedef void _PeriodicTimerCallback(Timer timer);
441
442 /**
443 * A [Timer] class for periodic callbacks that takes zones into account.
444 */
445 class _PeriodicZoneTimer implements Timer {
446 final _Zone _zone;
447 final _PeriodicTimerCallback _callback;
448 Timer _timer;
449
450 _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
451 _zone.expectCallback();
452 _timer = _createPeriodicTimer(duration, this._run);
453 }
454
455 void _run(Timer timer) {
456 assert(identical(_timer, timer));
457 _zone.executePeriodicCallbackGuarded(() { _callback(this); });
458 }
459
460 void cancel() {
461 if (_timer.isActive) _zone.cancelCallbackExpectation();
462 _timer.cancel();
463 }
464
465 bool get isActive => _timer.isActive;
466 }
467 581
468 /** 582 /**
469 * Runs [body] in its own zone. 583 * Runs [body] in its own zone.
470 * 584 *
471 * If [onError] is non-null the zone is considered an error zone. All uncaught 585 * If [onError] is non-null the zone is considered an error zone. All uncaught
472 * errors, synchronous or asynchronous, in the zone are caught and handled 586 * errors, synchronous or asynchronous, in the zone are caught and handled
473 * by the callback. 587 * by the callback.
474 * 588 *
475 * The [onDone] handler (if non-null) is invoked when the zone has no more
476 * outstanding callbacks. *Deprecated*: this method is less useful than it
477 * seems, because it assumes that every registered callback is always invoked.
478 * There are, however, many *valid* reasons not to complete futures or to abort
479 * a future-chain. In general it is a bad idea to rely on `onDone`.
480 *
481 * The [onRunAsync] handler (if non-null) is invoked when the [body] executes
482 * [runAsync]. The handler is invoked in the outer zone and can therefore
483 * execute [runAsync] without recursing. The given callback must be
484 * executed eventually. Otherwise the nested zone will not complete. It must be
485 * executed only once.
486 *
487 * Examples:
488 *
489 * runZonedExperimental(() {
490 * new Future(() { throw "asynchronous error"; });
491 * }, onError: print); // Will print "asynchronous error".
492 *
493 * The following example prints "1", "2", "3", "4" in this order.
494 *
495 * runZonedExperimental(() {
496 * print(1);
497 * new Future.value(3).then(print);
498 * }, onDone: () { print(4); });
499 * print(2);
500 *
501 * Errors may never cross error-zone boundaries. This is intuitive for leaving 589 * Errors may never cross error-zone boundaries. This is intuitive for leaving
502 * a zone, but it also applies for errors that would enter an error-zone. 590 * a zone, but it also applies for errors that would enter an error-zone.
503 * Errors that try to cross error-zone boundaries are considered uncaught. 591 * Errors that try to cross error-zone boundaries are considered uncaught.
504 * 592 *
505 * var future = new Future.value(499); 593 * var future = new Future.value(499);
506 * runZonedExperimental(() { 594 * runZonedExperimental(() {
507 * future = future.then((_) { throw "error in first error-zone"; }); 595 * future = future.then((_) { throw "error in first error-zone"; });
508 * runZonedExperimental(() { 596 * runZonedExperimental(() {
509 * future = future.catchError((e) { print("Never reached!"); }); 597 * future = future.catchError((e) { print("Never reached!"); });
510 * }, onError: (e) { print("unused error handler"); }); 598 * }, onError: (e) { print("unused error handler"); });
511 * }, onError: (e) { print("catches error of first error-zone."); }); 599 * }, onError: (e) { print("catches error of first error-zone."); });
512 * 600 *
601 * Example:
602 *
603 * runZonedExperimental(() {
604 * new Future(() { throw "asynchronous error"; });
605 * }, onError: print); // Will print "asynchronous error".
606 */
607 dynamic runZoned(body(),
608 { Map<Symbol, dynamic> zoneValues,
609 ZoneDescription zoneDescription,
610 void onError(error) }) {
611 HandleUncaughtErrorHandler errorHandler;
612 if (onError != null) {
613 errorHandler = (Zone self, ZoneDelegate parent, Zone zone, error) {
614 try {
615 return parent.zone.run1(onError, error);
616 } catch(e, s) {
617 if (identical(e, error)) {
618 return parent.handleUncaughtError(zone, error);
619 } else {
620 return parent.handleUncaughtError(zone, _asyncError(e, s));
621 }
622 }
623 };
624 }
625 if (zoneDescription == null) {
626 zoneDescription = new ZoneDescription(handleUncaughtError: errorHandler);
627 } else if (errorHandler != null) {
628 zoneDescription =
629 new ZoneDescription.from(zoneDescription,
630 handleUncaughtError: errorHandler);
631 }
632 Zone zone = Zone.current.fork(zoneValues, zoneDescription);
633 if (onError != null) {
634 return zone.runGuarded(body);
635 } else {
636 return zone.run(body);
637 }
638 }
639
640 /**
641 * Deprecated. Use `runZoned` instead or create your own [ZoneDescription].
642 *
643 * The [onRunAsync] handler (if non-null) is invoked when the [body] executes
644 * [runAsync]. The handler is invoked in the outer zone and can therefore
645 * execute [runAsync] without recursing. The given callback must be
646 * executed eventually. Otherwise the nested zone will not complete. It must be
647 * executed only once.
648 *
513 * The following example prints the stack trace whenever a callback is 649 * The following example prints the stack trace whenever a callback is
514 * registered using [runAsync] (which is also used by [Completer]s and 650 * registered using [runAsync] (which is also used by [Completer]s and
515 * [StreamController]s. 651 * [StreamController]s.
516 * 652 *
517 * printStackTrace() { try { throw 0; } catch(e, s) { print(s); } } 653 * printStackTrace() { try { throw 0; } catch(e, s) { print(s); } }
518 * runZonedExperimental(body, onRunAsync: (callback) { 654 * runZonedExperimental(body, onRunAsync: (callback) {
519 * printStackTrace(); 655 * printStackTrace();
520 * runAsync(callback); 656 * runAsync(callback);
521 * }); 657 * });
658 *
659 * Note: the `onDone` handler is ignored.
522 */ 660 */
661 @deprecated
523 runZonedExperimental(body(), 662 runZonedExperimental(body(),
524 { void onRunAsync(void callback()), 663 { void onRunAsync(void callback()),
525 void onError(error), 664 void onError(error),
526 void onDone() }) { 665 void onDone() }) {
666 if (onRunAsync == null) {
667 return runZoned(body, onError: onError);
668 }
669 HandleUncaughtErrorHandler errorHandler;
670 if (onError != null) {
671 errorHandler = (Zone self, ZoneDelegate parent, Zone zone, error) {
672 try {
673 return parent.zone.run1(onError, error);
674 } catch(e, s) {
675 if (identical(e, error)) {
676 return parent.handleUncaughtError(zone, error);
677 } else {
678 return parent.handleUncaughtError(zone, _asyncError(e, s));
679 }
680 }
681 };
682 }
683 ScheduleMicrotaskHandler asyncHandler;
527 if (onRunAsync != null) { 684 if (onRunAsync != null) {
528 _RunAsyncZone zone = new _RunAsyncZone(_Zone._current, onRunAsync); 685 asyncHandler = (Zone self, ZoneDelegate parent, Zone zone, f()) {
529 return zone._runUnguarded(() { 686 parent.zone.run1(onRunAsync, () => zone.runGuarded(f));
530 return runZonedExperimental(body, onError: onError, onDone: onDone); 687 };
531 });
532 } 688 }
533 689 ZoneDescription description =
534 // TODO(floitsch): we probably still want to install a new Zone. 690 new ZoneDescription(handleUncaughtError: errorHandler,
535 if (onError == null && onDone == null) return body(); 691 scheduleMicrotask: asyncHandler);
536 if (onError == null) { 692 Zone zone = Zone.current.fork(null, description);
537 _WaitForCompletionZone zone = 693 if (onError != null) {
538 new _WaitForCompletionZone(_Zone._current, onDone); 694 return zone.runGuarded(body);
539 return zone.runWaitForCompletion(body); 695 } else {
696 return zone.run(body);
540 } 697 }
541 if (onDone == null) onDone = _nullDoneHandler;
542 _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
543 return zone.runWaitForCompletion(body);
544 } 698 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698