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

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

Issue 17098007: Reapply "Zone support for Futures, Timer, Streams." (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: 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/timer.dart ('k') | tests/isolate/isolate.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of dart.async;
6
7 /**
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,
10 * the callback of a `future.then` is executed in the same zone as the one where
11 * the `then` was invoked.
12 */
13 abstract class _Zone {
14 /// The currently running zone.
15 static _Zone _current = new _DefaultZone();
16
17 static _Zone get current => _current;
18
19 void handleUncaughtError(error);
20
21 /**
22 * Returns true if `this` and [otherZone] are in the same error zone.
23 */
24 bool inSameErrorZone(_Zone otherZone);
25
26 /**
27 * Returns a zone for reentry in the zone.
28 *
29 * The returned zone is equivalent to `this` (and frequently is indeed
30 * `this`).
31 *
32 * The main purpose of this method is to allow `this` to attach debugging
33 * information to the returned zone.
34 */
35 _Zone fork();
36
37 /**
38 * Tells the zone that it needs to wait for one more callback before it is
39 * done.
40 *
41 * Use [executeCallback] or [cancelCallbackExpectation] when the callback is e xecuted
42 * (or canceled).
43 */
44 void expectCallback();
45
46 /**
47 * Tells the zone not to wait for a callback anymore.
48 *
49 * Prefer calling [executeCallback], instead. This method is mostly useful
50 * for repeated callbacks (for example with [Timer.periodic]). In this case
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
56 /**
57 * Executes the given callback in this zone.
58 *
59 * Decrements the number of callbacks this zone is waiting for (see
60 * [expectCallback]).
61 */
62 void executeCallback(void fun());
63
64 /**
65 * Same as [executeCallback] but catches uncaught errors and gives them to
66 * [handleUncaughtError].
67 */
68 void executeCallbackGuarded(void fun());
69
70 /**
71 * Same as [executeCallback] but does not decrement the number of
72 * callbacks this zone is waiting for (see [expectCallback]).
73 */
74 void executePeriodicCallback(void fun());
75
76 /**
77 * Same as [executePeriodicCallback] but catches uncaught errors and gives
78 * them to [handleUncaughtError].
79 */
80 void executePeriodicCallbackGuarded(void fun());
81
82 /**
83 * Runs [fun] asynchronously in this zone.
84 */
85 void runAsync(void fun());
86
87 /**
88 * Creates a Timer where the callback is executed in this zone.
89 */
90 Timer createTimer(Duration duration, void callback());
91
92 /**
93 * Creates a periodic Timer where the callback is executed in this zone.
94 */
95 Timer createPeriodicTimer(Duration duration, void callback(Timer timer));
96
97 /**
98 * The error zone is the one that is responsible for dealing with uncaught
99 * errors. Errors are not allowed to cross zones with different error-zones.
100 */
101 _Zone get _errorZone;
102
103 /**
104 * Adds [child] as a child of `this`.
105 *
106 * This usually means that the [child] is in the asynchronous dynamic extent
107 * of `this`.
108 */
109 void _addChild(_Zone child);
110
111 /**
112 * Removes [child] from `this`' children.
113 *
114 * This usually means that the [child] has finished executing and is done.
115 */
116 void _removeChild(_Zone child);
117 }
118
119 /**
120 * Basic implementation of a [_Zone]. This class is intended for subclassing.
121 */
122 class _ZoneBase implements _Zone {
123 /// The parent zone. [null] if `this` is the default zone.
124 final _Zone _parentZone;
125
126 /// The children of this zone. A child's [_parentZone] is `this`.
127 // TODO(floitsch): this should be a double-linked list.
128 final List<_Zone> _children = <_Zone>[];
129
130 /// The number of outstanding (asynchronous) callbacks. As long as the
131 /// number is greater than 0 it means that the zone is not done yet.
132 int _openCallbacks = 0;
133
134 bool _isExecutingCallback = false;
135
136 _ZoneBase(this._parentZone) {
137 _parentZone._addChild(this);
138 }
139
140 _ZoneBase._defaultZone() : _parentZone = null {
141 assert(this is _DefaultZone);
142 }
143
144 _Zone get _errorZone => _parentZone._errorZone;
145
146 void handleUncaughtError(error) {
147 _parentZone.handleUncaughtError(error);
148 }
149
150 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
151
152 _Zone fork() => this;
153
154 expectCallback() => _openCallbacks++;
155
156 cancelCallbackExpectation() {
157 _openCallbacks--;
158 _checkIfDone();
159 }
160
161 /**
162 * Cleans up this zone when it is done.
163 *
164 * This releases internal memore structures that are no longer necessary.
165 *
166 * A zone is done when its dynamic extent has finished executing and
167 * there are no outstanding asynchronous callbacks.
168 */
169 _dispose() {
170 if (_parentZone != null) {
171 _parentZone._removeChild(this);
172 }
173 }
174
175 /**
176 * Checks if the zone is done and doesn't have any outstanding callbacks
177 * anymore.
178 *
179 * This method is called when an operation has decremented the
180 * outstanding-callback count, or when a child has been removed.
181 */
182 void _checkIfDone() {
183 if (!_isExecutingCallback && _openCallbacks == 0 && _children.isEmpty) {
184 _dispose();
185 }
186 }
187
188 /**
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--;
196 this._runUnguarded(fun);
197 }
198
199 /**
200 * Same as [executeCallback] but catches uncaught errors and gives them to
201 * [handleUncaughtError].
202 */
203 void executeCallbackGuarded(void fun()) {
204 _openCallbacks--;
205 this._runGuarded(fun);
206 }
207
208 /**
209 * Same as [executeCallback] but doesn't decrement the open-callback counter.
210 */
211 void executePeriodicCallback(void fun()) {
212 this._runUnguarded(fun);
213 }
214
215 /**
216 * Same as [executePeriodicCallback] but catches uncaught errors and gives
217 * them to [handleUncaughtError].
218 */
219 void executePeriodicCallbackGuarded(void fun()) {
220 this._runGuarded(fun);
221 }
222
223 _runInZone(fun(), bool handleUncaught) {
224 if (identical(_Zone._current, this)
225 && !handleUncaught
226 && _isExecutingCallback) {
227 // No need to go through a try/catch.
228 return fun();
229 }
230
231 _Zone oldZone = _Zone._current;
232 _Zone._current = this;
233 // 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
235 // 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
237 // (probably) in the same zone and have an _openCallbacks > 0.
238 bool oldIsExecuting = _isExecutingCallback;
239 _isExecutingCallback = true;
240 // TODO(430): remove second try when VM bug is fixed.
241 try {
242 try {
243 return fun();
244 } catch(e, s) {
245 if (handleUncaught) {
246 handleUncaughtError(_asyncError(e, s));
247 } else {
248 rethrow;
249 }
250 }
251 } finally {
252 _isExecutingCallback = oldIsExecuting;
253 _Zone._current = oldZone;
254 _checkIfDone();
255 }
256 }
257
258 /**
259 * Runs the function and catches uncaught errors.
260 *
261 * Uncaught errors are given to [handleUncaughtError].
262 */
263 _runGuarded(void fun()) {
264 return _runInZone(fun, true);
265 }
266
267 /**
268 * Runs the function but doesn't catch uncaught errors.
269 */
270 _runUnguarded(void fun()) {
271 return _runInZone(fun, false);
272 }
273
274 runAsync(void fun()) {
275 _openCallbacks++;
276 _scheduleAsyncCallback(() {
277 _openCallbacks--;
278 _runGuarded(fun);
279 });
280 }
281
282 Timer createTimer(Duration duration, void callback()) {
283 return new _ZoneTimer(this, duration, callback);
284 }
285
286 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
287 return new _PeriodicZoneTimer(this, duration, callback);
288 }
289
290 void _addChild(_Zone child) {
291 _children.add(child);
292 }
293
294 void _removeChild(_Zone child) {
295 assert(!_children.isEmpty);
296 // Children are usually added and removed fifo or filo.
297 if (identical(_children.last, child)) {
298 _children.length--;
299 _checkIfDone();
300 return;
301 }
302 for (int i = 0; i < _children.length; i++) {
303 if (identical(_children[i], child)) {
304 _children[i] = _children[_children.length - 1];
305 _children.length--;
306 // No need to check for done, as otherwise _children.last above would
307 // have triggered.
308 assert(!_children.isEmpty);
309 return;
310 }
311 }
312 throw new ArgumentError(child);
313 }
314 }
315
316 /**
317 * The default-zone that conceptually surrounds the `main` function.
318 */
319 class _DefaultZone extends _ZoneBase {
320 _DefaultZone() : super._defaultZone();
321
322 _Zone get _errorZone => this;
323
324 handleUncaughtError(error) {
325 _scheduleAsyncCallback(() {
326 print("Uncaught Error: ${error}");
327 var trace = getAttachedStackTrace(error);
328 _attachStackTrace(error, null);
329 if (trace != null) {
330 print("Stack Trace:\n$trace\n");
331 }
332 throw error;
333 });
334 }
335 }
336
337 typedef void _CompletionCallback();
338
339 /**
340 * A zone that executes a callback when the zone is dead.
341 */
342 class _WaitForCompletionZone extends _ZoneBase {
343 final _CompletionCallback _onDone;
344
345 _WaitForCompletionZone(_Zone parentZone, this._onDone) : super(parentZone);
346
347 /**
348 * Runs the given function asynchronously. Executes the [_onDone] callback
349 * when the zone is done.
350 */
351 runWaitForCompletion(void fun()) {
352 return this._runUnguarded(fun);
353 }
354
355 _dispose() {
356 super._dispose();
357 _onDone();
358 }
359
360 String toString() => "WaitForCompletion ${super.toString()}";
361 }
362
363 typedef void _HandleErrorCallback(error);
364
365 /**
366 * A zone that collects all uncaught errors and provides them in a stream.
367 * The stream is closed when the zone is done.
368 */
369 class _CatchErrorsZone extends _WaitForCompletionZone {
370 final _HandleErrorCallback _handleError;
371
372 _CatchErrorsZone(_Zone parentZone, this._handleError, void onDone())
373 : super(parentZone, onDone);
374
375 _Zone get _errorZone => this;
376
377 handleUncaughtError(error) {
378 try {
379 _handleError(error);
380 } catch(e, s) {
381 if (identical(e, s)) {
382 _parentZone.handleUncaughtError(error);
383 } else {
384 _parentZone.handleUncaughtError(_asyncError(e, s));
385 }
386 }
387 }
388
389 /**
390 * Runs the given function asynchronously. Executes the [_onDone] callback
391 * when the zone is done.
392 */
393 runWaitForCompletion(void fun()) {
394 return this._runGuarded(fun);
395 }
396
397 String toString() => "WithErrors ${super.toString()}";
398 }
399
400 typedef void _TimerCallback();
401
402 /**
403 * A [Timer] class that takes zones into account.
404 */
405 class _ZoneTimer implements Timer {
406 final _Zone _zone;
407 final _TimerCallback _callback;
408 Timer _timer;
409 bool _isDone = false;
410
411 _ZoneTimer(this._zone, Duration duration, this._callback) {
412 _zone.expectCallback();
413 _timer = _createTimer(duration, this.run);
414 }
415
416 void run() {
417 _isDone = true;
418 _zone.executeCallbackGuarded(_callback);
419 }
420
421 void cancel() {
422 if (!_isDone) _zone.cancelCallbackExpectation();
423 _isDone = true;
424 _timer.cancel();
425 }
426 }
427
428 typedef void _PeriodicTimerCallback(Timer timer);
429
430 /**
431 * A [Timer] class for periodic callbacks that takes zones into account.
432 */
433 class _PeriodicZoneTimer implements Timer {
434 final _Zone _zone;
435 final _PeriodicTimerCallback _callback;
436 Timer _timer;
437 bool _isDone = false;
438
439 _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
440 _zone.expectCallback();
441 _timer = _createPeriodicTimer(duration, this.run);
442 }
443
444 void run(Timer timer) {
445 assert(identical(_timer, timer));
446 _zone.executePeriodicCallbackGuarded(() { _callback(this); });
447 }
448
449 void cancel() {
450 if (!_isDone) _zone.cancelCallbackExpectation();
451 _isDone = true;
452 _timer.cancel();
453 }
454 }
455
456 /**
457 * Runs [body] in its own zone.
458 *
459 * 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
461 * by the callback.
462 *
463 * [onDone] (if non-null) is invoked when the zone has no more outstanding
464 * callbacks.
465 *
466 * Examples:
467 *
468 * runZonedExperimental(() {
469 * new Future(() { throw "asynchronous error"; });
470 * }, onError: print); // Will print "asynchronous error".
471 *
472 * The following example prints "1", "2", "3", "4" in this order.
473 *
474 * runZonedExperimental(() {
475 * print(1);
476 * new Future.value(3).then(print);
477 * }, onDone: () { print(4); });
478 * print(2);
479 *
480 * 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.
482 * Errors that try to cross error-zone boundaries are considered uncaught.
483 *
484 * var future = new Future.value(499);
485 * runZonedExperimental(() {
486 * future = future.then((_) { throw "error in first error-zone"; });
487 * runZonedExperimental(() {
488 * future = future.catchError((e) { print("Never reached!"); });
489 * }, onError: (e) { print("unused error handler"); });
490 * }, onError: (e) { print("catches error of first error-zone."); });
491 *
492 */
493 runZonedExperimental(body(), { void onError(error), void onDone() }) {
494 // TODO(floitsch): we probably still want to install a new Zone.
495 if (onError == null && onDone == null) return body();
496 if (onError == null) {
497 _WaitForCompletionZone zone =
498 new _WaitForCompletionZone(_Zone._current, onDone);
499 return zone.runWaitForCompletion(body);
500 }
501 if (onDone == null) onDone = _nullDoneHandler;
502 _CatchErrorsZone zone = new _CatchErrorsZone(_Zone._current, onError, onDone);
503 return zone.runWaitForCompletion(body);
504 }
OLDNEW
« no previous file with comments | « sdk/lib/async/timer.dart ('k') | tests/isolate/isolate.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698