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

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

Powered by Google App Engine
This is Rietveld 408576698