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

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

Powered by Google App Engine
This is Rietveld 408576698