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

Side by Side Diff: sdk/lib/async/zone.dart

Issue 17672002: Add runAsync interceptor. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Created 7 years, 6 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/event_loop.dart ('k') | tests/lib/async/intercept_run_async1_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 /** 7 /**
8 * A Zone represents the asynchronous version of a dynamic extent. Asynchronous 8 * 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, 9 * 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 10 * the callback of a `future.then` is executed in the same zone as the one where
(...skipping 20 matching lines...) Expand all
31 * 31 *
32 * The main purpose of this method is to allow `this` to attach debugging 32 * The main purpose of this method is to allow `this` to attach debugging
33 * information to the returned zone. 33 * information to the returned zone.
34 */ 34 */
35 _Zone fork(); 35 _Zone fork();
36 36
37 /** 37 /**
38 * Tells the zone that it needs to wait for one more callback before it is 38 * Tells the zone that it needs to wait for one more callback before it is
39 * done. 39 * done.
40 * 40 *
41 * Use [executeCallback] or [cancelCallbackExpectation] when the callback is e xecuted 41 * Use [executeCallback] or [cancelCallbackExpectation] when the callback is
42 * (or canceled). 42 * executed (or canceled).
43 */ 43 */
44 void expectCallback(); 44 void expectCallback();
45 45
46 /** 46 /**
47 * Tells the zone not to wait for a callback anymore. 47 * Tells the zone not to wait for a callback anymore.
48 * 48 *
49 * Prefer calling [executeCallback], instead. This method is mostly useful 49 * Prefer calling [executeCallback], instead. This method is mostly useful
50 * for repeated callbacks (for example with [Timer.periodic]). In this case 50 * for repeated callbacks (for example with [Timer.periodic]). In this case
51 * one should should call [expectCallback] when the repeated callback is 51 * one should should call [expectCallback] when the repeated callback is
52 * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled. 52 * initiated, and [cancelCallbackExpectation] when the [Timer] is canceled.
53 */ 53 */
54 void cancelCallbackExpectation(); 54 void cancelCallbackExpectation();
55 55
56 /** 56 /**
57 * Executes the given callback in this zone. 57 * Executes the given callback [f] in this zone.
58 * 58 *
59 * Decrements the number of callbacks this zone is waiting for (see 59 * Decrements the number of callbacks this zone is waiting for (see
60 * [expectCallback]). 60 * [expectCallback]).
61 */ 61 */
62 void executeCallback(void fun()); 62 void executeCallback(void f());
63 63
64 /** 64 /**
65 * Same as [executeCallback] but catches uncaught errors and gives them to 65 * Same as [executeCallback] but catches uncaught errors and gives them to
66 * [handleUncaughtError]. 66 * [handleUncaughtError].
67 */ 67 */
68 void executeCallbackGuarded(void fun()); 68 void executeCallbackGuarded(void f());
69 69
70 /** 70 /**
71 * Same as [executeCallback] but does not decrement the number of 71 * Same as [executeCallback] but does not decrement the number of
72 * callbacks this zone is waiting for (see [expectCallback]). 72 * callbacks this zone is waiting for (see [expectCallback]).
73 */ 73 */
74 void executePeriodicCallback(void fun()); 74 void executePeriodicCallback(void f());
75 75
76 /** 76 /**
77 * Same as [executePeriodicCallback] but catches uncaught errors and gives 77 * Same as [executePeriodicCallback] but catches uncaught errors and gives
78 * them to [handleUncaughtError]. 78 * them to [handleUncaughtError].
79 */ 79 */
80 void executePeriodicCallbackGuarded(void fun()); 80 void executePeriodicCallbackGuarded(void f());
81 81
82 /** 82 /**
83 * Runs [fun] asynchronously in this zone. 83 * Executes [f] in `this` zone.
84 *
85 * The behavior of this method should be the same as
86 * [executePeriodicCallback] except that it can have a return value.
87 *
88 * Returns the result of the invocation.
84 */ 89 */
85 void runAsync(void fun()); 90 runFromChildZone(f());
91
92 /**
93 * Same as [runFromChildZone] but catches uncaught errors and gives them to
94 * [handleUncaughtError].
95 */
96 runFromChildZoneGuarded(f());
97
98 /**
99 * Runs [f] asynchronously in [zone].
100 */
101 void runAsync(void f(), _Zone zone);
86 102
87 /** 103 /**
88 * Creates a Timer where the callback is executed in this zone. 104 * Creates a Timer where the callback is executed in this zone.
89 */ 105 */
90 Timer createTimer(Duration duration, void callback()); 106 Timer createTimer(Duration duration, void callback());
91 107
92 /** 108 /**
93 * Creates a periodic Timer where the callback is executed in this zone. 109 * Creates a periodic Timer where the callback is executed in this zone.
94 */ 110 */
95 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)); 111 Timer createPeriodicTimer(Duration duration, void callback(Timer timer));
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 * 194 *
179 * This method is called when an operation has decremented the 195 * This method is called when an operation has decremented the
180 * outstanding-callback count, or when a child has been removed. 196 * outstanding-callback count, or when a child has been removed.
181 */ 197 */
182 void _checkIfDone() { 198 void _checkIfDone() {
183 if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) { 199 if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) {
184 _dispose(); 200 _dispose();
185 } 201 }
186 } 202 }
187 203
188 /** 204 void executeCallback(void f()) {
189 * Executes the given callback in this zone.
190 *
191 * Decrements the open-callback counter and checks (after the call) if the
192 * zone is done.
193 */
194 void executeCallback(void fun()) {
195 _openCallbacks--; 205 _openCallbacks--;
196 this._runUnguarded(fun); 206 this._runUnguarded(f);
197 } 207 }
198 208
199 /** 209 void executeCallbackGuarded(void f()) {
200 * Same as [executeCallback] but catches uncaught errors and gives them to
201 * [handleUncaughtError].
202 */
203 void executeCallbackGuarded(void fun()) {
204 _openCallbacks--; 210 _openCallbacks--;
205 this._runGuarded(fun); 211 this._runGuarded(f);
206 } 212 }
207 213
208 /** 214 void executePeriodicCallback(void f()) {
209 * Same as [executeCallback] but doesn't decrement the open-callback counter. 215 this._runUnguarded(f);
210 */
211 void executePeriodicCallback(void fun()) {
212 this._runUnguarded(fun);
213 } 216 }
214 217
215 /** 218 void executePeriodicCallbackGuarded(void f()) {
216 * Same as [executePeriodicCallback] but catches uncaught errors and gives 219 this._runGuarded(f);
217 * them to [handleUncaughtError].
218 */
219 void executePeriodicCallbackGuarded(void fun()) {
220 this._runGuarded(fun);
221 } 220 }
222 221
223 _runInZone(fun(), bool handleUncaught) { 222 runFromChildZone(f()) => this._runUnguarded(f);
223 runFromChildZoneGuarded(f()) => this._runGuarded(f);
224
225 _runInZone(f(), bool handleUncaught) {
224 if (identical(_Zone._current, this) 226 if (identical(_Zone._current, this)
225 && !handleUncaught 227 && !handleUncaught
226 && _isExecutingCallback) { 228 && _isExecutingCallback) {
227 // No need to go through a try/catch. 229 // No need to go through a try/catch.
228 return fun(); 230 return f();
229 } 231 }
230 232
231 _Zone oldZone = _Zone._current; 233 _Zone oldZone = _Zone._current;
232 _Zone._current = this; 234 _Zone._current = this;
233 // While we are executing the function we don't want to have other 235 // While we are executing the function we don't want to have other
234 // synchronous calls to think that they closed the zone. By incrementing 236 // synchronous calls to think that they closed the zone. By incrementing
235 // the _openCallbacks count we make sure that their test will fail. 237 // the _openCallbacks count we make sure that their test will fail.
236 // As a side effect it will make nested calls faster since they are 238 // As a side effect it will make nested calls faster since they are
237 // (probably) in the same zone and have an _openCallbacks > 0. 239 // (probably) in the same zone and have an _openCallbacks > 0.
238 bool oldIsExecuting = _isExecutingCallback; 240 bool oldIsExecuting = _isExecutingCallback;
239 _isExecutingCallback = true; 241 _isExecutingCallback = true;
240 // TODO(430): remove second try when VM bug is fixed. 242 // TODO(430): remove second try when VM bug is fixed.
241 try { 243 try {
242 try { 244 try {
243 return fun(); 245 return f();
244 } catch(e, s) { 246 } catch(e, s) {
245 if (handleUncaught) { 247 if (handleUncaught) {
246 handleUncaughtError(_asyncError(e, s)); 248 handleUncaughtError(_asyncError(e, s));
247 } else { 249 } else {
248 rethrow; 250 rethrow;
249 } 251 }
250 } 252 }
251 } finally { 253 } finally {
252 _isExecutingCallback = oldIsExecuting; 254 _isExecutingCallback = oldIsExecuting;
253 _Zone._current = oldZone; 255 _Zone._current = oldZone;
254 _checkIfDone(); 256 _checkIfDone();
255 } 257 }
256 } 258 }
257 259
258 /** 260 /**
259 * Runs the function and catches uncaught errors. 261 * Runs the function and catches uncaught errors.
260 * 262 *
261 * Uncaught errors are given to [handleUncaughtError]. 263 * Uncaught errors are given to [handleUncaughtError].
262 */ 264 */
263 _runGuarded(void fun()) { 265 _runGuarded(void f()) {
264 return _runInZone(fun, true); 266 return _runInZone(f, true);
265 } 267 }
266 268
267 /** 269 /**
268 * Runs the function but doesn't catch uncaught errors. 270 * Runs the function but doesn't catch uncaught errors.
269 */ 271 */
270 _runUnguarded(void fun()) { 272 _runUnguarded(void f()) {
271 return _runInZone(fun, false); 273 return _runInZone(f, false);
272 } 274 }
273 275
274 runAsync(void fun()) { 276 runAsync(void f(), _Zone zone) => _parentZone.runAsync(f, zone);
275 _openCallbacks++;
276 _scheduleAsyncCallback(() {
277 _openCallbacks--;
278 _runGuarded(fun);
279 });
280 }
281 277
278 // TODO(floitsch): the zone should just forward to the parent zone. The
279 // default zone should then create the _ZoneTimer.
282 Timer createTimer(Duration duration, void callback()) { 280 Timer createTimer(Duration duration, void callback()) {
283 return new _ZoneTimer(this, duration, callback); 281 return new _ZoneTimer(this, duration, callback);
284 } 282 }
285 283
284 // TODO(floitsch): the zone should just forward to the parent zone. The
285 // default zone should then create the _ZoneTimer.
286 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) { 286 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
287 return new _PeriodicZoneTimer(this, duration, callback); 287 return new _PeriodicZoneTimer(this, duration, callback);
288 } 288 }
289 289
290 void _addChild(_Zone child) { 290 void _addChild(_Zone child) {
291 // TODO(floitsch): the zone should just increment a counter, but not keep
292 // a reference to the child.
291 _children.add(child); 293 _children.add(child);
292 } 294 }
293 295
294 void _removeChild(_Zone child) { 296 void _removeChild(_Zone child) {
295 assert(!_children.isEmpty); 297 assert(!_children.isEmpty);
296 // Children are usually added and removed fifo or filo. 298 // Children are usually added and removed fifo or filo.
297 if (identical(_children.last, child)) { 299 if (identical(_children.last, child)) {
298 _children.length--; 300 _children.length--;
299 _checkIfDone(); 301 _checkIfDone();
300 return; 302 return;
(...skipping 24 matching lines...) Expand all
325 _scheduleAsyncCallback(() { 327 _scheduleAsyncCallback(() {
326 print("Uncaught Error: ${error}"); 328 print("Uncaught Error: ${error}");
327 var trace = getAttachedStackTrace(error); 329 var trace = getAttachedStackTrace(error);
328 _attachStackTrace(error, null); 330 _attachStackTrace(error, null);
329 if (trace != null) { 331 if (trace != null) {
330 print("Stack Trace:\n$trace\n"); 332 print("Stack Trace:\n$trace\n");
331 } 333 }
332 throw error; 334 throw error;
333 }); 335 });
334 } 336 }
337
338 void runAsync(void f(), _Zone zone) {
339 if (identical(this, zone)) {
340 // No need to go through the zone when it's the default zone anyways.
341 _scheduleAsyncCallback(f);
342 return;
343 }
344 zone.expectCallback();
345 _scheduleAsyncCallback(() {
346 zone.executeCallbackGuarded(f);
347 });
348 }
335 } 349 }
336 350
337 typedef void _CompletionCallback(); 351 typedef void _CompletionCallback();
338 352
339 /** 353 /**
340 * A zone that executes a callback when the zone is dead. 354 * A zone that executes a callback when the zone is dead.
341 */ 355 */
342 class _WaitForCompletionZone extends _ZoneBase { 356 class _WaitForCompletionZone extends _ZoneBase {
343 final _CompletionCallback _onDone; 357 final _CompletionCallback _onDone;
344 358
345 _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone); 359 _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
346 360
347 /** 361 /**
348 * Runs the given function asynchronously. Executes the [_onDone] callback 362 * Runs the given function asynchronously. Executes the [_onDone] callback
349 * when the zone is done. 363 * when the zone is done.
350 */ 364 */
351 runWaitForCompletion(void fun()) { 365 runWaitForCompletion(void f()) {
352 return this._runUnguarded(fun); 366 return this._runUnguarded(f);
353 } 367 }
354 368
355 _dispose() { 369 _dispose() {
356 super._dispose(); 370 super._dispose();
357 _onDone(); 371 _onDone();
358 } 372 }
359 373
360 String toString() => "WaitForCompletion ${super.toString()}"; 374 String toString() => "WaitForCompletion ${super.toString()}";
361 } 375 }
362 376
363 typedef void _HandleErrorCallback(error); 377 typedef void _HandleErrorCallback(error);
364 378
365 /** 379 /**
366 * A zone that collects all uncaught errors and provides them in a stream. 380 * A zone that collects all uncaught errors and provides them in a stream.
367 * The stream is closed when the zone is done. 381 * The stream is closed when the zone is done.
368 */ 382 */
369 class _CatchErrorsZone extends _WaitForCompletionZone { 383 class _CatchErrorsZone extends _WaitForCompletionZone {
370 final _HandleErrorCallback _handleError; 384 final _HandleErrorCallback _handleError;
371 385
372 _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone()) 386 _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone())
373 : super(parentZone, onDone); 387 : super(parentZone, onDone);
374 388
375 _Zone get _errorZone => this; 389 _Zone get _errorZone => this;
376 390
377 handleUncaughtError(error) { 391 handleUncaughtError(error) {
378 try { 392 try {
379 _handleError(error); 393 _handleError(error);
380 } catch(e, s) { 394 } catch(e, s) {
381 if (identical(e, s)) { 395 if (identical(e, s)) {
382 _parentZone.handleUncaughtError(error); 396 _parentZone.handleUncaughtError(error);
383 } else { 397 } else {
384 _parentZone.handleUncaughtError(_asyncError(e, s)); 398 _parentZone.handleUncaughtError(_asyncError(e, s));
385 } 399 }
386 } 400 }
387 } 401 }
388 402
389 /** 403 /**
390 * Runs the given function asynchronously. Executes the [_onDone] callback 404 * Runs the given function asynchronously. Executes the [_onDone] callback
391 * when the zone is done. 405 * when the zone is done.
392 */ 406 */
393 runWaitForCompletion(void fun()) { 407 runWaitForCompletion(void f()) {
394 return this._runGuarded(fun); 408 return this._runGuarded(f);
395 } 409 }
396 410
397 String toString() => "WithErrors ${super.toString()}"; 411 String toString() => "CatchErrors ${super.toString()}";
412 }
413
414 typedef void _RunAsyncInterceptor(void callback());
415
416 class _RunAsyncZone extends _ZoneBase {
417 final _RunAsyncInterceptor _runAsyncInterceptor;
418
419 _RunAsyncZone(_Zone parentZone, this._runAsyncInterceptor)
420 : super(parentZone);
421
422 void runAsync(void callback(), _Zone zone) {
423 zone.expectCallback();
424 _parentZone.runFromChildZone(() {
425 _runAsyncInterceptor(() => zone.executeCallbackGuarded(callback));
426 });
427 }
398 } 428 }
399 429
400 typedef void _TimerCallback(); 430 typedef void _TimerCallback();
401 431
402 /** 432 /**
403 * A [Timer] class that takes zones into account. 433 * A [Timer] class that takes zones into account.
404 */ 434 */
405 class _ZoneTimer implements Timer { 435 class _ZoneTimer implements Timer {
406 final _Zone _zone; 436 final _Zone _zone;
407 final _TimerCallback _callback; 437 final _TimerCallback _callback;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 } 483 }
454 } 484 }
455 485
456 /** 486 /**
457 * Runs [body] in its own zone. 487 * Runs [body] in its own zone.
458 * 488 *
459 * If [onError] is non-null the zone is considered an error zone. All uncaught 489 * If [onError] is non-null the zone is considered an error zone. All uncaught
460 * errors, synchronous or asynchronous, in the zone are caught and handled 490 * errors, synchronous or asynchronous, in the zone are caught and handled
461 * by the callback. 491 * by the callback.
462 * 492 *
463 * [onDone] (if non-null) is invoked when the zone has no more outstanding 493 * The [onDone] handler (if non-null) is invoked when the zone has no more
464 * callbacks. 494 * outstanding callbacks.
495 *
496 * The [onRunAsync] handler (if non-null) is invoked when the [body] executes
497 * [runAsync]. The handler is invoked in the outer zone and can therefore
498 * execute [runAsync] without recursing. The given callback must be
499 * executed eventually. Otherwise the nested zone will not complete. It must be
500 * executed only once.
465 * 501 *
466 * Examples: 502 * Examples:
467 * 503 *
468 * runZonedExperimental(() { 504 * runZonedExperimental(() {
469 * new Future(() { throw "asynchronous error"; }); 505 * new Future(() { throw "asynchronous error"; });
470 * }, onError: print); // Will print "asynchronous error". 506 * }, onError: print); // Will print "asynchronous error".
471 * 507 *
472 * The following example prints "1", "2", "3", "4" in this order. 508 * The following example prints "1", "2", "3", "4" in this order.
473 * 509 *
474 * runZonedExperimental(() { 510 * runZonedExperimental(() {
475 * print(1); 511 * print(1);
476 * new Future.value(3).then(print); 512 * new Future.value(3).then(print);
477 * }, onDone: () { print(4); }); 513 * }, onDone: () { print(4); });
478 * print(2); 514 * print(2);
479 * 515 *
480 * Errors may never cross error-zone boundaries. This is intuitive for leaving 516 * Errors may never cross error-zone boundaries. This is intuitive for leaving
481 * a zone, but it also applies for errors that would enter an error-zone. 517 * a zone, but it also applies for errors that would enter an error-zone.
482 * Errors that try to cross error-zone boundaries are considered uncaught. 518 * Errors that try to cross error-zone boundaries are considered uncaught.
483 * 519 *
484 * var future = new Future.value(499); 520 * var future = new Future.value(499);
485 * runZonedExperimental(() { 521 * runZonedExperimental(() {
486 * future = future.then((_) { throw "error in first error-zone"; }); 522 * future = future.then((_) { throw "error in first error-zone"; });
487 * runZonedExperimental(() { 523 * runZonedExperimental(() {
488 * future = future.catchError((e) { print("Never reached!"); }); 524 * future = future.catchError((e) { print("Never reached!"); });
489 * }, onError: (e) { print("unused error handler"); }); 525 * }, onError: (e) { print("unused error handler"); });
490 * }, onError: (e) { print("catches error of first error-zone."); }); 526 * }, onError: (e) { print("catches error of first error-zone."); });
491 * 527 *
528 * The following example prints the stack trace whenever a callback is
529 * registered using [runAsync] (which is also used by [Completer]s and
530 * [StreamController]s.
531 *
532 * printStackTrace() { try { throw 0; } catch(e, s) { print(s); } }
533 * runZonedExperimental(body, onRunAsync: (callback) {
534 * printStackTrace();
535 * runAsync(callback);
536 * });
492 */ 537 */
493 runZonedExperimental(body(), { void onError(error), void onDone() }) { 538 runZonedExperimental(body(),
539 { void onRunAsync(void callback()),
540 void onError(error),
541 void onDone() }) {
542 if (onRunAsync != null) {
543 _RunAsyncZone zone = new _RunAsyncZone(_Zone._current, onRunAsync);
544 return zone._runUnguarded(() {
545 return runZonedExperimental(body, onError: onError, onDone: onDone);
546 });
547 }
548
494 // TODO(floitsch): we probably still want to install a new Zone. 549 // TODO(floitsch): we probably still want to install a new Zone.
495 if (onError == null && onDone == null) return body(); 550 if (onError == null && onDone == null) return body();
496 if (onError == null) { 551 if (onError == null) {
497 _WaitForCompletionZone zone = 552 _WaitForCompletionZone zone =
498 new _WaitForCompletionZone(_Zone._current, onDone); 553 new _WaitForCompletionZone(_Zone._current, onDone);
499 return zone.runWaitForCompletion(body); 554 return zone.runWaitForCompletion(body);
500 } 555 }
501 if (onDone == null) onDone = _nullDoneHandler; 556 if (onDone == null) onDone = _nullDoneHandler;
502 _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone); 557 _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
503 return zone.runWaitForCompletion(body); 558 return zone.runWaitForCompletion(body);
504 } 559 }
OLDNEW
« no previous file with comments | « sdk/lib/async/event_loop.dart ('k') | tests/lib/async/intercept_run_async1_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698