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

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

Issue 14973006: Zone support for Futures. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebase 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/future_impl.dart ('k') | tests/lib/async/catch_errors2_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
(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 [unexpectCallback] when the callback is executed
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 [unexpectCallback] when the [Timer] is canceled.
53 */
54 void unexpectCallback();
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 does not decrement the number of
66 * callbacks this zone is waiting for (see [expectCallback]).
67 */
68 void executePeriodicCallback(void fun());
69
70 /**
71 * Runs [fun] asynchronously in this zone.
72 */
73 void runAsync(void fun());
74
75 /**
76 * The error zone is the one that is responsible for dealing with uncaught
77 * errors. Errors are not allowed to cross zones with different error-zones.
78 */
79 _Zone get _errorZone;
80
81 /**
82 * Adds [child] as a child of `this`.
83 *
84 * This usually means that the [child] is in the asynchronous dynamic extent
85 * of `this`.
86 */
87 void _addChild(_Zone child);
88
89 /**
90 * Removes [child] from `this`' children.
91 *
92 * This usually means that the [child] has finished executing and is done.
93 */
94 void _removeChild(_Zone child);
95 }
96
97 /**
98 * Basic implementation of a [_Zone]. This class is intended for subclassing.
99 */
100 class _ZoneBase implements _Zone {
101 /// The parent zone. [null] if `this` is the default zone.
102 final _Zone _parentZone;
103
104 /// The children of this zone. A child's [_parentZone] is `this`.
105 // TODO(floitsch): this should be a double-linked list.
106 final List<_Zone> _children = <_Zone>[];
107
108 /// The number of outstanding (asynchronous) callbacks. As long as the
109 /// number is greater than 0 it means that the zone is not done yet.
110 int _openCallbacks = 0;
111
112 _ZoneBase(this._parentZone) {
113 _parentZone._addChild(this);
114 }
115
116 _ZoneBase._defaultZone() : _parentZone = null {
117 assert(this is _DefaultZone);
118 }
119
120 _Zone get _errorZone => _parentZone._errorZone;
121
122 void handleUncaughtError(error) {
123 _parentZone.handleUncaughtError(error);
124 }
125
126 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
127
128 _Zone fork() => this;
129
130 expectCallback() => _openCallbacks++;
131
132 unexpectCallback() {
133 _openCallbacks--;
134 _checkIfDone();
135 }
136
137 /**
138 * Cleans up this zone when it is done.
139 *
140 * This releases internal memore structures that are no longer necessary.
141 *
142 * A zone is done when its dynamic extent has finished executing and
143 * there are no outstanding asynchronous callbacks.
144 */
145 _dispose() {
146 if (_parentZone != null) {
147 _parentZone._removeChild(this);
148 }
149 }
150
151 /**
152 * Checks if the zone is done and doesn't have any outstanding callbacks
153 * anymore.
154 *
155 * This method is called when an operation has decremented the
156 * outstanding-callback count, or when a child has been removed.
157 */
158 void _checkIfDone() {
159 if (_openCallbacks == 0 && _children.isEmpty) {
160 _dispose();
161 }
162 }
163
164 /**
165 * Executes the given callback in this zone.
166 *
167 * Decrements the open-callback counter and checks (after the call) if the
168 * zone is done.
169 */
170 void executeCallback(void fun()) {
171 _openCallbacks--;
172 _runInZone(fun);
173 }
174
175 /**
176 * Same as [executeCallback] but doesn't decrement the open-callback counter.
177 */
178 void executePeriodicCallback(void fun()) {
179 _runInZone(fun);
180 }
181
182 _runInZone(fun()) {
183 if (identical(_Zone._current, this) && _openCallbacks != 0) return fun();
184 return _runGuarded(fun);
185 }
186
187 _runGuarded(void fun()) {
188 _Zone oldZone = _Zone._current;
189 _Zone._current = this;
190 // While we are executing the function we don't want to have other
191 // synchronous calls to think that they closed the zone. By incrementing
192 // the _openCallbacks count we make sure that their test will fail.
193 // As a side effect it will make nested calls faster since they are
194 // (probably) in the same zone and have an _openCallbacks > 0.
195 _openCallbacks++;
196 try {
197 return fun();
198 } finally {
199 _openCallbacks--;
200 _Zone._current = oldZone;
201 _checkIfDone();
202 }
203 }
204
205 runAsync(void fun()) {
206 _openCallbacks++;
207 _scheduleAsyncCallback(() {
208 _openCallbacks--;
209 try {
210 _runInZone(fun);
211 } catch(e, s) {
212 handleUncaughtError(_asyncError(e, s));
213 }
214 });
215 }
216
217 void _addChild(_Zone child) {
218 _children.add(child);
219 }
220
221 void _removeChild(_Zone child) {
222 assert(!_children.isEmpty);
223 // Children are usually added and removed fifo or filo.
224 if (identical(_children.last, child)) {
225 _children.length--;
226 _checkIfDone();
227 return;
228 }
229 for (int i = 0; i < _children.length; i++) {
230 if (identical(_children[i], child)) {
231 _children[i] = _children[_children.length - 1];
232 _children.length--;
233 // No need to check for done, as otherwise _children.last above would
234 // have triggered.
235 assert(!_children.isEmpty);
236 return;
237 }
238 }
239 throw new ArgumentError(child);
240 }
241 }
242
243 /**
244 * The default-zone that conceptually surrounds the `main` function.
245 */
246 class _DefaultZone extends _ZoneBase {
247 _DefaultZone() : super._defaultZone();
248
249 _Zone get _errorZone => this;
250
251 handleUncaughtError(error) {
252 _scheduleAsyncCallback(() {
253 print("Uncaught Error: ${error}");
254 var trace = getAttachedStackTrace(error);
255 _attachStackTrace(error, null);
256 if (trace != null) {
257 print("Stack Trace:\n$trace\n");
258 }
259 throw error;
260 });
261 }
262 }
263
264 /**
265 * A zone that can execute a callback (through a future) when the zone is dead.
266 */
267 class _WaitForCompletionZone extends _ZoneBase {
268 final Completer _doneCompleter = new Completer();
269
270 _WaitForCompletionZone(_Zone parentZone) : super(parentZone);
271
272 /**
273 * Runs the given function asynchronously and returns a future that is
274 * completed with `null` once the zone is done.
275 */
276 Future runWaitForCompletion(void fun()) {
277 _runInZone(() {
278 try {
279 fun();
280 } catch (e, s) {
281 handleUncaughtError(_asyncError(e, s));
282 }
283 });
284 return _doneCompleter.future;
285 }
286
287 _dispose() {
288 super._dispose();
289 _doneCompleter.complete();
290 }
291
292 String toString() => "WaitForCompletion ${super.toString()}";
293 }
294
295 /**
296 * A zone that collects all uncaught errors and provides them in a stream.
297 * The stream is closed when the zone is done.
298 */
299 class _CatchErrorsZone extends _WaitForCompletionZone {
300 final StreamController errorsController = new StreamController();
301
302 Stream get errors => errorsController.stream;
303
304 _CatchErrorsZone(_Zone parentZone) : super(parentZone);
305
306 _Zone get _errorZone => this;
307
308 handleUncaughtError(error) {
309 errorsController.add(error);
310 }
311
312 Future runWaitForCompletion(void fun()) {
313 super.runWaitForCompletion(fun).whenComplete(() {
314 errorsController.close();
315 });
316 }
317
318 String toString() => "WithErrors ${super.toString()}";
319 }
320
321 Stream catchErrors(void body()) {
322 _Zone catchErrorsZone = new _CatchErrorsZone(_Zone._current);
323 catchErrorsZone.runWaitForCompletion(body);
324 return catchErrorsZone.errors;
325 }
326
327 Future waitForCompletion(void body()) {
328 _Zone zone = new _WaitForCompletionZone(_Zone._current);
329 return zone.runWaitForCompletion(body);
330 }
OLDNEW
« no previous file with comments | « sdk/lib/async/future_impl.dart ('k') | tests/lib/async/catch_errors2_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698