| OLD | NEW |
| (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 } |
| OLD | NEW |