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

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

Issue 15764003: Add Zone support for Timers. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix bug and add tests. 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
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 catches uncaught errors and gives them to
66 * [handleUncaughtError].
67 */
68 void executeGuardedCallback(void fun());
Lasse Reichstein Nielsen 2013/05/29 08:04:59 Name annoys me. It's not the callback which has th
floitsch 2013/05/29 15:21:29 changed to executeCallbackGuarded
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());
Lasse Reichstein Nielsen 2013/05/29 08:04:59 execueCallbackPeriodically?
floitsch 2013/05/29 15:21:29 no. The callback is not executed periodically. it'
75
76 /**
77 * Same as [executePeriodicCallback] but catches uncaught errors and gives
78 * them to [handleUncaughtError].
79 */
80 void executeGuardedPeriodicCallback(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 _ZoneBase(this._parentZone) {
135 _parentZone._addChild(this);
136 }
137
138 _ZoneBase._defaultZone() : _parentZone = null {
139 assert(this is _DefaultZone);
140 }
141
142 _Zone get _errorZone => _parentZone._errorZone;
143
144 void handleUncaughtError(error) {
145 _parentZone.handleUncaughtError(error);
146 }
147
148 bool inSameErrorZone(_Zone otherZone) => _errorZone == otherZone._errorZone;
149
150 _Zone fork() => this;
151
152 expectCallback() => _openCallbacks++;
153
154 unexpectCallback() {
155 _openCallbacks--;
156 _checkIfDone();
157 }
158
159 /**
160 * Cleans up this zone when it is done.
161 *
162 * This releases internal memore structures that are no longer necessary.
163 *
164 * A zone is done when its dynamic extent has finished executing and
165 * there are no outstanding asynchronous callbacks.
166 */
167 _dispose() {
168 if (_parentZone != null) {
169 _parentZone._removeChild(this);
170 }
171 }
172
173 /**
174 * Checks if the zone is done and doesn't have any outstanding callbacks
175 * anymore.
176 *
177 * This method is called when an operation has decremented the
178 * outstanding-callback count, or when a child has been removed.
179 */
180 void _checkIfDone() {
181 if (_openCallbacks == 0 && _children.isEmpty) {
182 _dispose();
183 }
184 }
185
186 /**
187 * Executes the given callback in this zone.
188 *
189 * Decrements the open-callback counter and checks (after the call) if the
190 * zone is done.
191 */
192 void executeCallback(void fun()) {
193 _openCallbacks--;
194 _runInZone(fun);
195 }
196
197 /**
198 * Same as [executeCallback] but catches uncaught errors and gives them to
199 * [handleUncaughtError].
200 */
201 void executeGuardedCallback(void fun()) {
202 _openCallbacks--;
203 _runGuarded(fun);
204 }
205
206 /**
207 * Same as [executeCallback] but doesn't decrement the open-callback counter.
208 */
209 void executePeriodicCallback(void fun()) {
210 _runInZone(fun);
211 }
212
213 /**
214 * Same as [executePeriodicCallback] but catches uncaught errors and gives
215 * them to [handleUncaughtError].
216 */
217 void executeGuardedPeriodicCallback(void fun()) {
218 _runGuarded(fun);
219 }
220
221 _runInZone(fun()) {
222 if (identical(_Zone._current, this) && _openCallbacks != 0) return fun();
223
224 _Zone oldZone = _Zone._current;
225 _Zone._current = this;
226 // While we are executing the function we don't want to have other
227 // synchronous calls to think that they closed the zone. By incrementing
228 // the _openCallbacks count we make sure that their test will fail.
229 // As a side effect it will make nested calls faster since they are
230 // (probably) in the same zone and have an _openCallbacks > 0.
231 _openCallbacks++;
232 try {
233 return fun();
234 } finally {
235 _openCallbacks--;
236 _Zone._current = oldZone;
237 _checkIfDone();
238 }
239 }
240
241 /**
242 * Runs the function and catches uncaught errors.
243 *
244 * Uncaught errors are given to [handleUncaughtError].
245 */
246 _runGuarded(void fun()) {
247 try {
248 _runInZone(fun);
249 } catch(e, s) {
250 handleUncaughtError(_asyncError(e, s));
251 }
252 }
253
254 runAsync(void fun()) {
255 _openCallbacks++;
256 _scheduleAsyncCallback(() {
257 _openCallbacks--;
258 _runGuarded(fun);
259 });
260 }
261
262 Timer createTimer(Duration duration, void callback()) {
263 return new _ZoneTimer(this, duration, callback);
264 }
265
266 Timer createPeriodicTimer(Duration duration, void callback(Timer timer)) {
267 return new _PeriodicZoneTimer(this, duration, callback);
268 }
269
270 void _addChild(_Zone child) {
271 _children.add(child);
272 }
273
274 void _removeChild(_Zone child) {
275 assert(!_children.isEmpty);
276 // Children are usually added and removed fifo or filo.
277 if (identical(_children.last, child)) {
278 _children.length--;
279 _checkIfDone();
280 return;
281 }
282 for (int i = 0; i < _children.length; i++) {
283 if (identical(_children[i], child)) {
284 _children[i] = _children[_children.length - 1];
285 _children.length--;
286 // No need to check for done, as otherwise _children.last above would
287 // have triggered.
288 assert(!_children.isEmpty);
289 return;
290 }
291 }
292 throw new ArgumentError(child);
293 }
294 }
295
296 /**
297 * The default-zone that conceptually surrounds the `main` function.
298 */
299 class _DefaultZone extends _ZoneBase {
300 _DefaultZone() : super._defaultZone();
301
302 _Zone get _errorZone => this;
303
304 handleUncaughtError(error) {
305 print("Uncaught Error: ${error}");
306 var trace = getAttachedStackTrace(error);
307 if (trace != null) {
308 print("Stack Trace:\n$trace\n");
309 }
310 throw error;
311 }
312 }
313
314 /**
315 * A zone that can execute a callback (through a future) when the zone is dead.
316 */
317 class _WaitForCompletionZone extends _ZoneBase {
318 final Completer _doneCompleter = new Completer();
319
320 _WaitForCompletionZone(_Zone parentZone) : super(parentZone);
321
322 /**
323 * Runs the given function asynchronously and returns a future that is
324 * completed with `null` once the zone is done.
325 */
326 Future runWaitForCompletion(void fun()) {
327 _runInZone(() {
328 try {
329 fun();
330 } catch (e, s) {
331 handleUncaughtError(_asyncError(e, s));
332 }
333 });
334 return _doneCompleter.future;
335 }
336
337 _dispose() {
338 super._dispose();
339 _doneCompleter.complete();
340 }
341
342 String toString() => "WaitForCompletion ${super.toString()}";
343 }
344
345 /**
346 * A zone that collects all uncaught errors and provides them in a stream.
347 * The stream is closed when the zone is done.
348 */
349 class _CatchErrorsZone extends _WaitForCompletionZone {
350 final StreamController errorsController = new StreamController();
351
352 Stream get errors => errorsController.stream;
353
354 _CatchErrorsZone(_Zone parentZone) : super(parentZone);
355
356 _Zone get _errorZone => this;
357
358 handleUncaughtError(error) {
359 errorsController.add(error);
360 }
361
362 Future runWaitForCompletion(void fun()) {
363 super.runWaitForCompletion(fun).whenComplete(() {
364 errorsController.close();
365 });
366 }
367
368 String toString() => "WithErrors ${super.toString()}";
369 }
370
371 typedef void _TimerCallback();
372
373 /**
374 * A [Timer] class that takes zones into account.
375 */
376 class _ZoneTimer implements Timer {
377 final _Zone _zone;
378 final _TimerCallback _callback;
379 Timer _timer;
380 bool _isDone = false;
381
382 _ZoneTimer(this._zone, Duration duration, this._callback) {
383 _zone.expectCallback();
384 _timer = _createTimer(duration, this.run);
385 }
386
387 void run() {
388 _isDone = true;
389 _zone.executeGuardedCallback(_callback);
390 }
391
392 void cancel() {
393 if (!_isDone) _zone.unexpectCallback();
394 _isDone = true;
395 _timer.cancel();
396 }
397 }
398
399 typedef void _PeriodicTimerCallback(Timer timer);
400
401 /**
402 * A [Timer] class for periodic callbacks that takes zones into account.
403 */
404 class _PeriodicZoneTimer implements Timer {
405 final _Zone _zone;
406 final _PeriodicTimerCallback _callback;
407 Timer _timer;
408 bool _isDone = false;
409
410 _PeriodicZoneTimer(this._zone, Duration duration, this._callback) {
411 _zone.expectCallback();
412 _timer = _createPeriodicTimer(duration, this.run);
413 }
414
415 void run(Timer timer) {
416 assert(identical(_timer, timer));
417 _zone.executeGuardedPeriodicCallback(() { _callback(this); });
418 }
419
420 void cancel() {
421 if (!_isDone) _zone.unexpectCallback();
422 _isDone = true;
423 _timer.cancel();
424 }
425 }
426
427 Stream catchErrors(void body()) {
428 _Zone catchErrorsZone = new _CatchErrorsZone(_Zone._current);
429 catchErrorsZone.runWaitForCompletion(body);
430 return catchErrorsZone.errors;
431 }
432
433 Future waitForCompletion(void body()) {
434 _Zone zone = new _WaitForCompletionZone(_Zone._current);
435 return zone.runWaitForCompletion(body);
436 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698