| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 library dart._isolate_helper; | 5 library dart._isolate_helper; |
| 6 | 6 |
| 7 import 'dart:_js_embedded_names' show | 7 import 'dart:_js_embedded_names' |
| 8 CLASS_ID_EXTRACTOR, | 8 show |
| 9 CLASS_FIELDS_EXTRACTOR, | 9 CLASS_ID_EXTRACTOR, |
| 10 CURRENT_SCRIPT, | 10 CLASS_FIELDS_EXTRACTOR, |
| 11 GLOBAL_FUNCTIONS, | 11 CURRENT_SCRIPT, |
| 12 INITIALIZE_EMPTY_INSTANCE, | 12 GLOBAL_FUNCTIONS, |
| 13 INSTANCE_FROM_CLASS_ID; | 13 INITIALIZE_EMPTY_INSTANCE, |
| 14 INSTANCE_FROM_CLASS_ID; |
| 14 | 15 |
| 15 import 'dart:async'; | 16 import 'dart:async'; |
| 16 import 'dart:collection' show Queue, HashMap; | 17 import 'dart:collection' show Queue, HashMap; |
| 17 import 'dart:isolate'; | 18 import 'dart:isolate'; |
| 18 import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData; | 19 import 'dart:_native_typed_data' show NativeByteBuffer, NativeTypedData; |
| 19 | 20 |
| 20 import 'dart:_js_helper' show | 21 import 'dart:_js_helper' show InternalMap, Null, Primitives, random64; |
| 21 InternalMap, | |
| 22 Null, | |
| 23 Primitives, | |
| 24 random64; | |
| 25 | 22 |
| 26 import 'dart:_foreign_helper' show JS, | 23 import 'dart:_foreign_helper' |
| 27 JS_CREATE_ISOLATE, | 24 show |
| 28 JS_CURRENT_ISOLATE_CONTEXT, | 25 JS, |
| 29 JS_CURRENT_ISOLATE, | 26 JS_CREATE_ISOLATE, |
| 30 JS_EMBEDDED_GLOBAL, | 27 JS_CURRENT_ISOLATE_CONTEXT, |
| 31 JS_SET_CURRENT_ISOLATE, | 28 JS_CURRENT_ISOLATE, |
| 32 IsolateContext; | 29 JS_EMBEDDED_GLOBAL, |
| 30 JS_SET_CURRENT_ISOLATE, |
| 31 IsolateContext; |
| 33 | 32 |
| 34 import 'dart:_interceptors' show Interceptor, | 33 import 'dart:_interceptors' |
| 35 JSArray, | 34 show |
| 36 JSExtendableArray, | 35 Interceptor, |
| 37 JSFixedArray, | 36 JSArray, |
| 38 JSIndexable, | 37 JSExtendableArray, |
| 39 JSMutableArray, | 38 JSFixedArray, |
| 40 JSObject; | 39 JSIndexable, |
| 41 | 40 JSMutableArray, |
| 41 JSObject; |
| 42 | 42 |
| 43 part 'isolate_serialization.dart'; | 43 part 'isolate_serialization.dart'; |
| 44 | 44 |
| 45 /** | 45 /** |
| 46 * Called by the compiler to support switching | 46 * Called by the compiler to support switching |
| 47 * between isolates when we get a callback from the DOM. | 47 * between isolates when we get a callback from the DOM. |
| 48 */ | 48 */ |
| 49 _callInIsolate(_IsolateContext isolate, Function function) { | 49 _callInIsolate(_IsolateContext isolate, Function function) { |
| 50 var result = isolate.eval(function); | 50 var result = isolate.eval(function); |
| 51 _globalState.topEventLoop.run(); | 51 _globalState.topEventLoop.run(); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 if (_globalState.isWorker) return; | 103 if (_globalState.isWorker) return; |
| 104 final rootContext = new _IsolateContext(); | 104 final rootContext = new _IsolateContext(); |
| 105 _globalState.rootContext = rootContext; | 105 _globalState.rootContext = rootContext; |
| 106 | 106 |
| 107 // BUG(5151491): Setting currentContext should not be necessary, but | 107 // BUG(5151491): Setting currentContext should not be necessary, but |
| 108 // because closures passed to the DOM as event handlers do not bind their | 108 // because closures passed to the DOM as event handlers do not bind their |
| 109 // isolate automatically we try to give them a reasonable context to live in | 109 // isolate automatically we try to give them a reasonable context to live in |
| 110 // by having a "default" isolate (the first one created). | 110 // by having a "default" isolate (the first one created). |
| 111 _globalState.currentContext = rootContext; | 111 _globalState.currentContext = rootContext; |
| 112 if (entry is _MainFunctionArgs) { | 112 if (entry is _MainFunctionArgs) { |
| 113 rootContext.eval(() { entry(args); }); | 113 rootContext.eval(() { |
| 114 entry(args); |
| 115 }); |
| 114 } else if (entry is _MainFunctionArgsMessage) { | 116 } else if (entry is _MainFunctionArgsMessage) { |
| 115 rootContext.eval(() { entry(args, null); }); | 117 rootContext.eval(() { |
| 118 entry(args, null); |
| 119 }); |
| 116 } else { | 120 } else { |
| 117 rootContext.eval(entry); | 121 rootContext.eval(entry); |
| 118 } | 122 } |
| 119 _globalState.topEventLoop.run(); | 123 _globalState.topEventLoop.run(); |
| 120 } | 124 } |
| 121 | 125 |
| 122 /******************************************************** | 126 /******************************************************** |
| 123 Inserted from lib/isolate/dart2js/isolateimpl.dart | 127 Inserted from lib/isolate/dart2js/isolateimpl.dart |
| 124 ********************************************************/ | 128 ********************************************************/ |
| 125 | 129 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 | 163 |
| 160 set _globalState(_Manager val) { | 164 set _globalState(_Manager val) { |
| 161 // TODO(vsm): This was changed from init.globalState. | 165 // TODO(vsm): This was changed from init.globalState. |
| 162 // See: https://github.com/dart-lang/dev_compiler/issues/164 | 166 // See: https://github.com/dart-lang/dev_compiler/issues/164 |
| 163 JS("void", "dart.globalState = #", val); | 167 JS("void", "dart.globalState = #", val); |
| 164 } | 168 } |
| 165 | 169 |
| 166 /** State associated with the current manager. See [globalState]. */ | 170 /** State associated with the current manager. See [globalState]. */ |
| 167 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? | 171 // TODO(sigmund): split in multiple classes: global, thread, main-worker states? |
| 168 class _Manager { | 172 class _Manager { |
| 169 | |
| 170 /** Next available isolate id within this [_Manager]. */ | 173 /** Next available isolate id within this [_Manager]. */ |
| 171 int nextIsolateId = 0; | 174 int nextIsolateId = 0; |
| 172 | 175 |
| 173 /** id assigned to this [_Manager]. */ | 176 /** id assigned to this [_Manager]. */ |
| 174 int currentManagerId = 0; | 177 int currentManagerId = 0; |
| 175 | 178 |
| 176 /** | 179 /** |
| 177 * Next available manager id. Only used by the main manager to assign a unique | 180 * Next available manager id. Only used by the main manager to assign a unique |
| 178 * id to each manager created by it. | 181 * id to each manager created by it. |
| 179 */ | 182 */ |
| (...skipping 27 matching lines...) Expand all Loading... |
| 207 * Registry of isolates. Isolates must be registered if, and only if, receive | 210 * Registry of isolates. Isolates must be registered if, and only if, receive |
| 208 * ports are alive. Normally no open receive-ports means that the isolate is | 211 * ports are alive. Normally no open receive-ports means that the isolate is |
| 209 * dead, but DOM callbacks could resurrect it. | 212 * dead, but DOM callbacks could resurrect it. |
| 210 */ | 213 */ |
| 211 Map<int, _IsolateContext> isolates; | 214 Map<int, _IsolateContext> isolates; |
| 212 | 215 |
| 213 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ | 216 /** Reference to the main [_Manager]. Null in the main [_Manager] itself. */ |
| 214 _MainManagerStub mainManager; | 217 _MainManagerStub mainManager; |
| 215 | 218 |
| 216 /// Registry of active Web Workers. Only used in the main [_Manager]. | 219 /// Registry of active Web Workers. Only used in the main [_Manager]. |
| 217 Map<int, dynamic /* Worker */> managers; | 220 Map<int, dynamic /* Worker */ > managers; |
| 218 | 221 |
| 219 /** The entry point given by [startRootIsolate]. */ | 222 /** The entry point given by [startRootIsolate]. */ |
| 220 final Function entry; | 223 final Function entry; |
| 221 | 224 |
| 222 _Manager(this.entry) { | 225 _Manager(this.entry) { |
| 223 _nativeDetectEnvironment(); | 226 _nativeDetectEnvironment(); |
| 224 topEventLoop = new _EventLoop(); | 227 topEventLoop = new _EventLoop(); |
| 225 isolates = new Map<int, _IsolateContext>(); | 228 isolates = new Map<int, _IsolateContext>(); |
| 226 managers = new Map<int, dynamic>(); | 229 managers = new Map<int, dynamic>(); |
| 227 if (isWorker) { // "if we are not the main manager ourself" is the intent. | 230 if (isWorker) { |
| 231 // "if we are not the main manager ourself" is the intent. |
| 228 mainManager = new _MainManagerStub(); | 232 mainManager = new _MainManagerStub(); |
| 229 _nativeInitWorkerMessageHandler(); | 233 _nativeInitWorkerMessageHandler(); |
| 230 } | 234 } |
| 231 } | 235 } |
| 232 | 236 |
| 233 void _nativeDetectEnvironment() { | 237 void _nativeDetectEnvironment() { |
| 234 bool isWindowDefined = globalWindow != null; | 238 bool isWindowDefined = globalWindow != null; |
| 235 bool isWorkerDefined = globalWorker != null; | 239 bool isWorkerDefined = globalWorker != null; |
| 236 | 240 |
| 237 isWorker = !isWindowDefined && globalPostMessageDefined; | 241 isWorker = !isWindowDefined && globalPostMessageDefined; |
| 238 supportsWorkers = isWorker | 242 supportsWorkers = |
| 239 || (isWorkerDefined && IsolateNatives.thisScript != null); | 243 isWorker || (isWorkerDefined && IsolateNatives.thisScript != null); |
| 240 fromCommandLine = !isWindowDefined && !isWorker; | 244 fromCommandLine = !isWindowDefined && !isWorker; |
| 241 } | 245 } |
| 242 | 246 |
| 243 void _nativeInitWorkerMessageHandler() { | 247 void _nativeInitWorkerMessageHandler() { |
| 244 var function = JS('', | 248 var function = JS( |
| 249 '', |
| 245 "(function (f, a) { return function (e) { f(a, e); }})(#, #)", | 250 "(function (f, a) { return function (e) { f(a, e); }})(#, #)", |
| 246 IsolateNatives._processWorkerMessage, | 251 IsolateNatives._processWorkerMessage, |
| 247 mainManager); | 252 mainManager); |
| 248 JS("void", r"#.onmessage = #", global, function); | 253 JS("void", r"#.onmessage = #", global, function); |
| 249 // We ensure dartPrint is defined so that the implementation of the Dart | 254 // We ensure dartPrint is defined so that the implementation of the Dart |
| 250 // print method knows what to call. | 255 // print method knows what to call. |
| 251 JS('', '''#.dartPrint = #.dartPrint || (function(serialize) { | 256 JS( |
| 257 '', |
| 258 '''#.dartPrint = #.dartPrint || (function(serialize) { |
| 252 return function (object) { | 259 return function (object) { |
| 253 var _self = #; | 260 var _self = #; |
| 254 if (_self.console && _self.console.log) { | 261 if (_self.console && _self.console.log) { |
| 255 _self.console.log(object) | 262 _self.console.log(object) |
| 256 } else { | 263 } else { |
| 257 _self.postMessage(serialize(object)); | 264 _self.postMessage(serialize(object)); |
| 258 } | 265 } |
| 259 } | 266 } |
| 260 })(#)''', global, global, global, _serializePrintMessage); | 267 })(#)''', |
| 268 global, |
| 269 global, |
| 270 global, |
| 271 _serializePrintMessage); |
| 261 } | 272 } |
| 262 | 273 |
| 263 static _serializePrintMessage(object) { | 274 static _serializePrintMessage(object) { |
| 264 return _serializeMessage({"command": "print", "msg": object}); | 275 return _serializeMessage({"command": "print", "msg": object}); |
| 265 } | 276 } |
| 266 | 277 |
| 267 /** | 278 /** |
| 268 * Close the worker running this code if all isolates are done and | 279 * Close the worker running this code if all isolates are done and |
| 269 * there are no active async JavaScript tasks still running. | 280 * there are no active async JavaScript tasks still running. |
| 270 */ | 281 */ |
| 271 void maybeCloseWorker() { | 282 void maybeCloseWorker() { |
| 272 if (isWorker | 283 if (isWorker && isolates.isEmpty && topEventLoop._activeJsAsyncCount == 0) { |
| 273 && isolates.isEmpty | |
| 274 && topEventLoop._activeJsAsyncCount == 0) { | |
| 275 mainManager.postMessage(_serializeMessage({'command': 'close'})); | 284 mainManager.postMessage(_serializeMessage({'command': 'close'})); |
| 276 } | 285 } |
| 277 } | 286 } |
| 278 } | 287 } |
| 279 | 288 |
| 280 /** Context information tracked for each isolate. */ | 289 /** Context information tracked for each isolate. */ |
| 281 class _IsolateContext implements IsolateContext { | 290 class _IsolateContext implements IsolateContext { |
| 282 /** Current isolate id. */ | 291 /** Current isolate id. */ |
| 283 final int id = _globalState.nextIsolateId++; | 292 final int id = _globalState.nextIsolateId++; |
| 284 | 293 |
| 285 /** Registry of receive ports currently active on this isolate. */ | 294 /** Registry of receive ports currently active on this isolate. */ |
| 286 final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>(); | 295 final Map<int, RawReceivePortImpl> ports = new Map<int, RawReceivePortImpl>(); |
| 287 | 296 |
| 288 /** Registry of weak receive ports currently active on this isolate. */ | 297 /** Registry of weak receive ports currently active on this isolate. */ |
| 289 final Set<int> weakPorts = new Set<int>(); | 298 final Set<int> weakPorts = new Set<int>(); |
| 290 | 299 |
| 291 /** Holds isolate globals (statics and top-level properties). */ | 300 /** Holds isolate globals (statics and top-level properties). */ |
| 292 // native object containing all globals of an isolate. | 301 // native object containing all globals of an isolate. |
| 293 final isolateStatics = JS_CREATE_ISOLATE(); | 302 final isolateStatics = JS_CREATE_ISOLATE(); |
| 294 | 303 |
| 295 final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort(); | 304 final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort(); |
| 296 | 305 |
| 297 final Capability pauseCapability = new Capability(); | 306 final Capability pauseCapability = new Capability(); |
| 298 final Capability terminateCapability = new Capability(); // License to kill. | 307 final Capability terminateCapability = new Capability(); // License to kill. |
| 299 | 308 |
| 300 /// Boolean flag set when the initial method of the isolate has been executed. | 309 /// Boolean flag set when the initial method of the isolate has been executed. |
| 301 /// | 310 /// |
| 302 /// Used to avoid considering the isolate dead when it has no open | 311 /// Used to avoid considering the isolate dead when it has no open |
| 303 /// receive ports and no scheduled timers, because it hasn't had time to | 312 /// receive ports and no scheduled timers, because it hasn't had time to |
| 304 /// create them yet. | 313 /// create them yet. |
| 305 bool initialized = false; | 314 bool initialized = false; |
| 306 | 315 |
| 307 // TODO(lrn): Store these in single "PauseState" object, so they don't take | 316 // TODO(lrn): Store these in single "PauseState" object, so they don't take |
| 308 // up as much room when not pausing. | 317 // up as much room when not pausing. |
| (...skipping 29 matching lines...) Expand all Loading... |
| 338 if (pauseTokens.add(resume) && !isPaused) { | 347 if (pauseTokens.add(resume) && !isPaused) { |
| 339 isPaused = true; | 348 isPaused = true; |
| 340 } | 349 } |
| 341 _updateGlobalState(); | 350 _updateGlobalState(); |
| 342 } | 351 } |
| 343 | 352 |
| 344 void removePause(Capability resume) { | 353 void removePause(Capability resume) { |
| 345 if (!isPaused) return; | 354 if (!isPaused) return; |
| 346 pauseTokens.remove(resume); | 355 pauseTokens.remove(resume); |
| 347 if (pauseTokens.isEmpty) { | 356 if (pauseTokens.isEmpty) { |
| 348 while(delayedEvents.isNotEmpty) { | 357 while (delayedEvents.isNotEmpty) { |
| 349 _IsolateEvent event = delayedEvents.removeLast(); | 358 _IsolateEvent event = delayedEvents.removeLast(); |
| 350 _globalState.topEventLoop.prequeue(event); | 359 _globalState.topEventLoop.prequeue(event); |
| 351 } | 360 } |
| 352 isPaused = false; | 361 isPaused = false; |
| 353 } | 362 } |
| 354 _updateGlobalState(); | 363 _updateGlobalState(); |
| 355 } | 364 } |
| 356 | 365 |
| 357 void addDoneListener(SendPort responsePort) { | 366 void addDoneListener(SendPort responsePort) { |
| 358 if (doneHandlers == null) { | 367 if (doneHandlers == null) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 369 doneHandlers.remove(responsePort); | 378 doneHandlers.remove(responsePort); |
| 370 } | 379 } |
| 371 | 380 |
| 372 void setErrorsFatal(Capability authentification, bool errorsAreFatal) { | 381 void setErrorsFatal(Capability authentification, bool errorsAreFatal) { |
| 373 if (terminateCapability != authentification) return; | 382 if (terminateCapability != authentification) return; |
| 374 this.errorsAreFatal = errorsAreFatal; | 383 this.errorsAreFatal = errorsAreFatal; |
| 375 } | 384 } |
| 376 | 385 |
| 377 void handlePing(SendPort responsePort, int pingType) { | 386 void handlePing(SendPort responsePort, int pingType) { |
| 378 if (pingType == Isolate.IMMEDIATE || | 387 if (pingType == Isolate.IMMEDIATE || |
| 379 (pingType == Isolate.BEFORE_NEXT_EVENT && | 388 (pingType == Isolate.BEFORE_NEXT_EVENT && !_isExecutingEvent)) { |
| 380 !_isExecutingEvent)) { | |
| 381 responsePort.send(null); | 389 responsePort.send(null); |
| 382 return; | 390 return; |
| 383 } | 391 } |
| 384 void respond() { responsePort.send(null); } | 392 void respond() { |
| 393 responsePort.send(null); |
| 394 } |
| 395 |
| 385 assert(pingType == Isolate.BEFORE_NEXT_EVENT); | 396 assert(pingType == Isolate.BEFORE_NEXT_EVENT); |
| 386 if (_scheduledControlEvents == null) { | 397 if (_scheduledControlEvents == null) { |
| 387 _scheduledControlEvents = new Queue(); | 398 _scheduledControlEvents = new Queue(); |
| 388 } | 399 } |
| 389 _scheduledControlEvents.addLast(respond); | 400 _scheduledControlEvents.addLast(respond); |
| 390 } | 401 } |
| 391 | 402 |
| 392 void handleKill(Capability authentification, int priority) { | 403 void handleKill(Capability authentification, int priority) { |
| 393 if (this.terminateCapability != authentification) return; | 404 if (this.terminateCapability != authentification) return; |
| 394 if (priority == Isolate.IMMEDIATE || | 405 if (priority == Isolate.IMMEDIATE || |
| 395 (priority == Isolate.BEFORE_NEXT_EVENT && | 406 (priority == Isolate.BEFORE_NEXT_EVENT && !_isExecutingEvent)) { |
| 396 !_isExecutingEvent)) { | |
| 397 kill(); | 407 kill(); |
| 398 return; | 408 return; |
| 399 } | 409 } |
| 400 assert(priority == Isolate.BEFORE_NEXT_EVENT); | 410 assert(priority == Isolate.BEFORE_NEXT_EVENT); |
| 401 if (_scheduledControlEvents == null) { | 411 if (_scheduledControlEvents == null) { |
| 402 _scheduledControlEvents = new Queue(); | 412 _scheduledControlEvents = new Queue(); |
| 403 } | 413 } |
| 404 _scheduledControlEvents.addLast(kill); | 414 _scheduledControlEvents.addLast(kill); |
| 405 } | 415 } |
| 406 | 416 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 424 } | 434 } |
| 425 if (JS('bool', '#.console && #.console.error', global, global)) { | 435 if (JS('bool', '#.console && #.console.error', global, global)) { |
| 426 JS('void', '#.console.error(#, #)', global, error, stackTrace); | 436 JS('void', '#.console.error(#, #)', global, error, stackTrace); |
| 427 } else { | 437 } else { |
| 428 print(error); | 438 print(error); |
| 429 if (stackTrace != null) print(stackTrace); | 439 if (stackTrace != null) print(stackTrace); |
| 430 } | 440 } |
| 431 return; | 441 return; |
| 432 } | 442 } |
| 433 List message = new List(2) | 443 List message = new List(2) |
| 434 ..[0] = error.toString() | 444 ..[0] = error.toString() |
| 435 ..[1] = (stackTrace == null) ? null : stackTrace.toString(); | 445 ..[1] = (stackTrace == null) ? null : stackTrace.toString(); |
| 436 for (SendPort port in errorPorts) port.send(message); | 446 for (SendPort port in errorPorts) port.send(message); |
| 437 } | 447 } |
| 438 | 448 |
| 439 /** | 449 /** |
| 440 * Run [code] in the context of the isolate represented by [this]. | 450 * Run [code] in the context of the isolate represented by [this]. |
| 441 */ | 451 */ |
| 442 dynamic eval(Function code) { | 452 dynamic eval(Function code) { |
| 443 var old = _globalState.currentContext; | 453 var old = _globalState.currentContext; |
| 444 _globalState.currentContext = this; | 454 _globalState.currentContext = this; |
| 445 this._setGlobals(); | 455 this._setGlobals(); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 RawReceivePortImpl lookup(int portId) => ports[portId]; | 527 RawReceivePortImpl lookup(int portId) => ports[portId]; |
| 518 | 528 |
| 519 void _addRegistration(int portId, RawReceivePortImpl port) { | 529 void _addRegistration(int portId, RawReceivePortImpl port) { |
| 520 if (ports.containsKey(portId)) { | 530 if (ports.containsKey(portId)) { |
| 521 throw new Exception("Registry: ports must be registered only once."); | 531 throw new Exception("Registry: ports must be registered only once."); |
| 522 } | 532 } |
| 523 ports[portId] = port; | 533 ports[portId] = port; |
| 524 } | 534 } |
| 525 | 535 |
| 526 /** Registers a port on this isolate. */ | 536 /** Registers a port on this isolate. */ |
| 527 void register(int portId, RawReceivePortImpl port) { | 537 void register(int portId, RawReceivePortImpl port) { |
| 528 _addRegistration(portId, port); | 538 _addRegistration(portId, port); |
| 529 _updateGlobalState(); | 539 _updateGlobalState(); |
| 530 } | 540 } |
| 531 | 541 |
| 532 /** | 542 /** |
| 533 * Registers a weak port on this isolate. | 543 * Registers a weak port on this isolate. |
| 534 * | 544 * |
| 535 * The port does not keep the isolate active. | 545 * The port does not keep the isolate active. |
| 536 */ | 546 */ |
| 537 void registerWeak(int portId, RawReceivePortImpl port) { | 547 void registerWeak(int portId, RawReceivePortImpl port) { |
| 538 weakPorts.add(portId); | 548 weakPorts.add(portId); |
| 539 _addRegistration(portId, port); | 549 _addRegistration(portId, port); |
| 540 } | 550 } |
| 541 | 551 |
| 542 void _updateGlobalState() { | 552 void _updateGlobalState() { |
| 543 if (ports.length - weakPorts.length > 0 || isPaused || !initialized) { | 553 if (ports.length - weakPorts.length > 0 || isPaused || !initialized) { |
| 544 _globalState.isolates[id] = this; // indicate this isolate is active | 554 _globalState.isolates[id] = this; // indicate this isolate is active |
| 545 } else { | 555 } else { |
| 546 kill(); | 556 kill(); |
| 547 } | 557 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 600 void prequeue(_IsolateEvent event) { | 610 void prequeue(_IsolateEvent event) { |
| 601 events.addFirst(event); | 611 events.addFirst(event); |
| 602 } | 612 } |
| 603 | 613 |
| 604 _IsolateEvent dequeue() { | 614 _IsolateEvent dequeue() { |
| 605 if (events.isEmpty) return null; | 615 if (events.isEmpty) return null; |
| 606 return events.removeFirst(); | 616 return events.removeFirst(); |
| 607 } | 617 } |
| 608 | 618 |
| 609 void checkOpenReceivePortsFromCommandLine() { | 619 void checkOpenReceivePortsFromCommandLine() { |
| 610 if (_globalState.rootContext != null | 620 if (_globalState.rootContext != null && |
| 611 && _globalState.isolates.containsKey(_globalState.rootContext.id) | 621 _globalState.isolates.containsKey(_globalState.rootContext.id) && |
| 612 && _globalState.fromCommandLine | 622 _globalState.fromCommandLine && |
| 613 && _globalState.rootContext.ports.isEmpty) { | 623 _globalState.rootContext.ports.isEmpty) { |
| 614 // We want to reach here only on the main [_Manager] and only | 624 // We want to reach here only on the main [_Manager] and only |
| 615 // on the command-line. In the browser the isolate might | 625 // on the command-line. In the browser the isolate might |
| 616 // still be alive due to DOM callbacks, but the presumption is | 626 // still be alive due to DOM callbacks, but the presumption is |
| 617 // that on the command-line, no future events can be injected | 627 // that on the command-line, no future events can be injected |
| 618 // into the event queue once it's empty. Node has setTimeout | 628 // into the event queue once it's empty. Node has setTimeout |
| 619 // so this presumption is incorrect there. We think(?) that | 629 // so this presumption is incorrect there. We think(?) that |
| 620 // in d8 this assumption is valid. | 630 // in d8 this assumption is valid. |
| 621 throw new Exception("Program exited with open ReceivePorts."); | 631 throw new Exception("Program exited with open ReceivePorts."); |
| 622 } | 632 } |
| 623 } | 633 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 638 * Runs multiple iterations of the run-loop. If possible, each iteration is | 648 * Runs multiple iterations of the run-loop. If possible, each iteration is |
| 639 * run asynchronously. | 649 * run asynchronously. |
| 640 */ | 650 */ |
| 641 void _runHelper() { | 651 void _runHelper() { |
| 642 if (globalWindow != null) { | 652 if (globalWindow != null) { |
| 643 // Run each iteration from the browser's top event loop. | 653 // Run each iteration from the browser's top event loop. |
| 644 next() { | 654 next() { |
| 645 if (!runIteration()) return; | 655 if (!runIteration()) return; |
| 646 Timer.run(next); | 656 Timer.run(next); |
| 647 } | 657 } |
| 658 |
| 648 next(); | 659 next(); |
| 649 } else { | 660 } else { |
| 650 // Run synchronously until no more iterations are available. | 661 // Run synchronously until no more iterations are available. |
| 651 while (runIteration()) {} | 662 while (runIteration()) {} |
| 652 } | 663 } |
| 653 } | 664 } |
| 654 | 665 |
| 655 /** | 666 /** |
| 656 * Call [_runHelper] but ensure that worker exceptions are propragated. | 667 * Call [_runHelper] but ensure that worker exceptions are propragated. |
| 657 */ | 668 */ |
| 658 void run() { | 669 void run() { |
| 659 if (!_globalState.isWorker) { | 670 if (!_globalState.isWorker) { |
| 660 _runHelper(); | 671 _runHelper(); |
| 661 } else { | 672 } else { |
| 662 try { | 673 try { |
| 663 _runHelper(); | 674 _runHelper(); |
| 664 } catch (e, trace) { | 675 } catch (e, trace) { |
| 665 _globalState.mainManager.postMessage(_serializeMessage( | 676 _globalState.mainManager.postMessage( |
| 666 {'command': 'error', 'msg': '$e\n$trace' })); | 677 _serializeMessage({'command': 'error', 'msg': '$e\n$trace'})); |
| 667 } | 678 } |
| 668 } | 679 } |
| 669 } | 680 } |
| 670 } | 681 } |
| 671 | 682 |
| 672 /** An event in the top-level event queue. */ | 683 /** An event in the top-level event queue. */ |
| 673 class _IsolateEvent { | 684 class _IsolateEvent { |
| 674 _IsolateContext isolate; | 685 _IsolateContext isolate; |
| 675 Function fn; | 686 Function fn; |
| 676 String message; | 687 String message; |
| 677 | 688 |
| 678 _IsolateEvent(this.isolate, this.fn, this.message); | 689 _IsolateEvent(this.isolate, this.fn, this.message); |
| 679 | 690 |
| 680 void process() { | 691 void process() { |
| 681 if (isolate.isPaused) { | 692 if (isolate.isPaused) { |
| 682 isolate.delayedEvents.add(this); | 693 isolate.delayedEvents.add(this); |
| 683 return; | 694 return; |
| 684 } | 695 } |
| 685 isolate.eval(fn); | 696 isolate.eval(fn); |
| 686 } | 697 } |
| 687 } | 698 } |
| 688 | 699 |
| 689 // "self" is a way to refer to the global context object that | 700 // "self" is a way to refer to the global context object that |
| 690 // works in HTML pages and in Web Workers. It does not work in d8, iojs | 701 // works in HTML pages and in Web Workers. It does not work in d8, iojs |
| 691 // and Firefox jsshell, because that would have been too easy. In iojs | 702 // and Firefox jsshell, because that would have been too easy. In iojs |
| 692 // "global" works. | 703 // "global" works. |
| 693 // | 704 // |
| 694 // See: http://www.w3.org/TR/workers/#the-global-scope | 705 // See: http://www.w3.org/TR/workers/#the-global-scope |
| 695 // and: http://www.w3.org/TR/Window/#dfn-self-attribute | 706 // and: http://www.w3.org/TR/Window/#dfn-self-attribute |
| 696 final global = | 707 final global = JS("", "typeof global == 'undefined' ? self : global"); |
| 697 JS("", "typeof global == 'undefined' ? self : global"); | |
| 698 | 708 |
| 699 /** A stub for interacting with the main manager. */ | 709 /** A stub for interacting with the main manager. */ |
| 700 class _MainManagerStub { | 710 class _MainManagerStub { |
| 701 void postMessage(msg) { | 711 void postMessage(msg) { |
| 702 JS("void", r"#.postMessage(#)", global, msg); | 712 JS("void", r"#.postMessage(#)", global, msg); |
| 703 } | 713 } |
| 704 } | 714 } |
| 705 | 715 |
| 706 const String _SPAWNED_SIGNAL = "spawned"; | 716 const String _SPAWNED_SIGNAL = "spawned"; |
| 707 const String _SPAWN_FAILED_SIGNAL = "spawn failed"; | 717 const String _SPAWN_FAILED_SIGNAL = "spawn failed"; |
| 708 | 718 |
| 709 get globalWindow { | 719 get globalWindow { |
| 710 return JS('', "#.window", global); | 720 return JS('', "#.window", global); |
| 711 } | 721 } |
| 712 | 722 |
| 713 get globalWorker { | 723 get globalWorker { |
| 714 return JS('', "#.Worker", global); | 724 return JS('', "#.Worker", global); |
| 715 } | 725 } |
| 726 |
| 716 bool get globalPostMessageDefined { | 727 bool get globalPostMessageDefined { |
| 717 return JS('bool', "!!#.postMessage", global); | 728 return JS('bool', "!!#.postMessage", global); |
| 718 } | 729 } |
| 719 | 730 |
| 720 typedef _MainFunction(); | 731 typedef _MainFunction(); |
| 721 typedef _MainFunctionArgs(args); | 732 typedef _MainFunctionArgs(args); |
| 722 typedef _MainFunctionArgsMessage(args, message); | 733 typedef _MainFunctionArgsMessage(args, message); |
| 723 | 734 |
| 724 /// Note: IsolateNatives depends on _globalState which is only set up correctly | 735 /// Note: IsolateNatives depends on _globalState which is only set up correctly |
| 725 /// when 'dart:isolate' has been imported. | 736 /// when 'dart:isolate' has been imported. |
| 726 class IsolateNatives { | 737 class IsolateNatives { |
| 727 | |
| 728 // We set [enableSpawnWorker] to true (not null) when calling isolate | 738 // We set [enableSpawnWorker] to true (not null) when calling isolate |
| 729 // primitives that require support for spawning workers. The field starts out | 739 // primitives that require support for spawning workers. The field starts out |
| 730 // by being null, and dart2js' type inference will track if it can have a | 740 // by being null, and dart2js' type inference will track if it can have a |
| 731 // non-null value. So by testing if this value is not null, we generate code | 741 // non-null value. So by testing if this value is not null, we generate code |
| 732 // that dart2js knows is dead when worker support isn't needed. | 742 // that dart2js knows is dead when worker support isn't needed. |
| 733 // TODO(herhut): Initialize this to false when able to track compile-time | 743 // TODO(herhut): Initialize this to false when able to track compile-time |
| 734 // constants. | 744 // constants. |
| 735 static var enableSpawnWorker; | 745 static var enableSpawnWorker; |
| 736 | 746 |
| 737 static String thisScript = computeThisScript(); | 747 static String thisScript = computeThisScript(); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 766 // TODO(ahe): The following is for supporting D8. We should move this code | 776 // TODO(ahe): The following is for supporting D8. We should move this code |
| 767 // to a helper library that is only loaded when testing on D8. | 777 // to a helper library that is only loaded when testing on D8. |
| 768 static String computeThisScriptD8() => computeThisScriptFromTrace(); | 778 static String computeThisScriptD8() => computeThisScriptFromTrace(); |
| 769 | 779 |
| 770 static String computeThisScriptFromTrace() { | 780 static String computeThisScriptFromTrace() { |
| 771 var stack = JS('String|Null', 'new Error().stack'); | 781 var stack = JS('String|Null', 'new Error().stack'); |
| 772 if (stack == null) { | 782 if (stack == null) { |
| 773 // According to Internet Explorer documentation, the stack | 783 // According to Internet Explorer documentation, the stack |
| 774 // property is not set until the exception is thrown. The stack | 784 // property is not set until the exception is thrown. The stack |
| 775 // property was not provided until IE10. | 785 // property was not provided until IE10. |
| 776 stack = JS('String|Null', | 786 stack = JS( |
| 777 '(function() {' | 787 'String|Null', |
| 778 'try { throw new Error() } catch(e) { return e.stack }' | 788 '(function() {' |
| 779 '})()'); | 789 'try { throw new Error() } catch(e) { return e.stack }' |
| 790 '})()'); |
| 780 if (stack == null) throw new UnsupportedError('No stack trace'); | 791 if (stack == null) throw new UnsupportedError('No stack trace'); |
| 781 } | 792 } |
| 782 var pattern, matches; | 793 var pattern, matches; |
| 783 | 794 |
| 784 // This pattern matches V8, Chrome, and Internet Explorer stack | 795 // This pattern matches V8, Chrome, and Internet Explorer stack |
| 785 // traces that look like this: | 796 // traces that look like this: |
| 786 // Error | 797 // Error |
| 787 // at methodName (URI:LINE:COLUMN) | 798 // at methodName (URI:LINE:COLUMN) |
| 788 pattern = JS('', | 799 pattern = |
| 789 r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")'); | 800 JS('', r'new RegExp("^ *at [^(]*\\((.*):[0-9]*:[0-9]*\\)$", "m")'); |
| 790 | |
| 791 | 801 |
| 792 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); | 802 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); |
| 793 if (matches != null) return JS('String', '#[1]', matches); | 803 if (matches != null) return JS('String', '#[1]', matches); |
| 794 | 804 |
| 795 // This pattern matches Firefox stack traces that look like this: | 805 // This pattern matches Firefox stack traces that look like this: |
| 796 // methodName@URI:LINE | 806 // methodName@URI:LINE |
| 797 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); | 807 pattern = JS('', r'new RegExp("^[^@]*@(.*):[0-9]*$", "m")'); |
| 798 | 808 |
| 799 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); | 809 matches = JS('JSExtendableArray|Null', '#.match(#)', stack, pattern); |
| 800 if (matches != null) return JS('String', '#[1]', matches); | 810 if (matches != null) return JS('String', '#[1]', matches); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 822 Function entryPoint = (functionName == null) | 832 Function entryPoint = (functionName == null) |
| 823 ? _globalState.entry | 833 ? _globalState.entry |
| 824 : _getJSFunctionFromName(functionName); | 834 : _getJSFunctionFromName(functionName); |
| 825 var args = msg['args']; | 835 var args = msg['args']; |
| 826 var message = _deserializeMessage(msg['msg']); | 836 var message = _deserializeMessage(msg['msg']); |
| 827 var isSpawnUri = msg['isSpawnUri']; | 837 var isSpawnUri = msg['isSpawnUri']; |
| 828 var startPaused = msg['startPaused']; | 838 var startPaused = msg['startPaused']; |
| 829 var replyTo = _deserializeMessage(msg['replyTo']); | 839 var replyTo = _deserializeMessage(msg['replyTo']); |
| 830 var context = new _IsolateContext(); | 840 var context = new _IsolateContext(); |
| 831 _globalState.topEventLoop.enqueue(context, () { | 841 _globalState.topEventLoop.enqueue(context, () { |
| 832 _startIsolate(entryPoint, args, message, | 842 _startIsolate( |
| 833 isSpawnUri, startPaused, replyTo); | 843 entryPoint, args, message, isSpawnUri, startPaused, replyTo); |
| 834 }, 'worker-start'); | 844 }, 'worker-start'); |
| 835 // Make sure we always have a current context in this worker. | 845 // Make sure we always have a current context in this worker. |
| 836 // TODO(7907): This is currently needed because we're using | 846 // TODO(7907): This is currently needed because we're using |
| 837 // Timers to implement Futures, and this isolate library | 847 // Timers to implement Futures, and this isolate library |
| 838 // implementation uses Futures. We should either stop using | 848 // implementation uses Futures. We should either stop using |
| 839 // Futures in this library, or re-adapt if Futures get a | 849 // Futures in this library, or re-adapt if Futures get a |
| 840 // different implementation. | 850 // different implementation. |
| 841 _globalState.currentContext = context; | 851 _globalState.currentContext = context; |
| 842 _globalState.topEventLoop.run(); | 852 _globalState.topEventLoop.run(); |
| 843 break; | 853 break; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 855 case 'close': | 865 case 'close': |
| 856 _globalState.managers.remove(workerIds[sender]); | 866 _globalState.managers.remove(workerIds[sender]); |
| 857 JS('void', '#.terminate()', sender); | 867 JS('void', '#.terminate()', sender); |
| 858 _globalState.topEventLoop.run(); | 868 _globalState.topEventLoop.run(); |
| 859 break; | 869 break; |
| 860 case 'log': | 870 case 'log': |
| 861 _log(msg['msg']); | 871 _log(msg['msg']); |
| 862 break; | 872 break; |
| 863 case 'print': | 873 case 'print': |
| 864 if (_globalState.isWorker) { | 874 if (_globalState.isWorker) { |
| 865 _globalState.mainManager.postMessage( | 875 _globalState.mainManager |
| 866 _serializeMessage({'command': 'print', 'msg': msg})); | 876 .postMessage(_serializeMessage({'command': 'print', 'msg': msg})); |
| 867 } else { | 877 } else { |
| 868 print(msg['msg']); | 878 print(msg['msg']); |
| 869 } | 879 } |
| 870 break; | 880 break; |
| 871 case 'error': | 881 case 'error': |
| 872 throw msg['msg']; | 882 throw msg['msg']; |
| 873 } | 883 } |
| 874 } | 884 } |
| 875 | 885 |
| 876 static handleSpawnWorkerRequest(msg) { | 886 static handleSpawnWorkerRequest(msg) { |
| 877 var replyPort = msg['replyPort']; | 887 var replyPort = msg['replyPort']; |
| 878 spawn(msg['functionName'], msg['uri'], | 888 spawn(msg['functionName'], msg['uri'], msg['args'], msg['msg'], false, |
| 879 msg['args'], msg['msg'], | 889 msg['isSpawnUri'], msg['startPaused']).then((msg) { |
| 880 false, msg['isSpawnUri'], msg['startPaused']).then((msg) { | |
| 881 replyPort.send(msg); | 890 replyPort.send(msg); |
| 882 }, onError: (String errorMessage) { | 891 }, onError: (String errorMessage) { |
| 883 replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]); | 892 replyPort.send([_SPAWN_FAILED_SIGNAL, errorMessage]); |
| 884 }); | 893 }); |
| 885 } | 894 } |
| 886 | 895 |
| 887 /** Log a message, forwarding to the main [_Manager] if appropriate. */ | 896 /** Log a message, forwarding to the main [_Manager] if appropriate. */ |
| 888 static _log(msg) { | 897 static _log(msg) { |
| 889 if (_globalState.isWorker) { | 898 if (_globalState.isWorker) { |
| 890 _globalState.mainManager.postMessage( | 899 _globalState.mainManager |
| 891 _serializeMessage({'command': 'log', 'msg': msg })); | 900 .postMessage(_serializeMessage({'command': 'log', 'msg': msg})); |
| 892 } else { | 901 } else { |
| 893 try { | 902 try { |
| 894 _consoleLog(msg); | 903 _consoleLog(msg); |
| 895 } catch (e, trace) { | 904 } catch (e, trace) { |
| 896 throw new Exception(trace); | 905 throw new Exception(trace); |
| 897 } | 906 } |
| 898 } | 907 } |
| 899 } | 908 } |
| 900 | 909 |
| 901 static void _consoleLog(msg) { | 910 static void _consoleLog(msg) { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 914 */ | 923 */ |
| 915 static String _getJSFunctionName(Function f) { | 924 static String _getJSFunctionName(Function f) { |
| 916 return JS("String|Null", r'#.$name', f); | 925 return JS("String|Null", r'#.$name', f); |
| 917 } | 926 } |
| 918 | 927 |
| 919 /** Create a new JavaScript object instance given its constructor. */ | 928 /** Create a new JavaScript object instance given its constructor. */ |
| 920 static dynamic _allocate(var ctor) { | 929 static dynamic _allocate(var ctor) { |
| 921 return JS("", "new #()", ctor); | 930 return JS("", "new #()", ctor); |
| 922 } | 931 } |
| 923 | 932 |
| 924 static Future<List> spawnFunction(void topLevelFunction(message), | 933 static Future<List> spawnFunction( |
| 925 var message, | 934 void topLevelFunction(message), var message, bool startPaused) { |
| 926 bool startPaused) { | |
| 927 IsolateNatives.enableSpawnWorker = true; | 935 IsolateNatives.enableSpawnWorker = true; |
| 928 final name = _getJSFunctionName(topLevelFunction); | 936 final name = _getJSFunctionName(topLevelFunction); |
| 929 if (name == null) { | 937 if (name == null) { |
| 930 throw new UnsupportedError( | 938 throw new UnsupportedError("only top-level functions can be spawned."); |
| 931 "only top-level functions can be spawned."); | |
| 932 } | 939 } |
| 933 bool isLight = false; | 940 bool isLight = false; |
| 934 bool isSpawnUri = false; | 941 bool isSpawnUri = false; |
| 935 return spawn(name, null, null, message, isLight, isSpawnUri, startPaused); | 942 return spawn(name, null, null, message, isLight, isSpawnUri, startPaused); |
| 936 } | 943 } |
| 937 | 944 |
| 938 static Future<List> spawnUri(Uri uri, List<String> args, var message, | 945 static Future<List> spawnUri( |
| 939 bool startPaused) { | 946 Uri uri, List<String> args, var message, bool startPaused) { |
| 940 IsolateNatives.enableSpawnWorker = true; | 947 IsolateNatives.enableSpawnWorker = true; |
| 941 bool isLight = false; | 948 bool isLight = false; |
| 942 bool isSpawnUri = true; | 949 bool isSpawnUri = true; |
| 943 return spawn(null, uri.toString(), args, message, | 950 return spawn( |
| 944 isLight, isSpawnUri, startPaused); | 951 null, uri.toString(), args, message, isLight, isSpawnUri, startPaused); |
| 945 } | 952 } |
| 946 | 953 |
| 947 // TODO(sigmund): clean up above, after we make the new API the default: | 954 // TODO(sigmund): clean up above, after we make the new API the default: |
| 948 | 955 |
| 949 /// If [uri] is `null` it is replaced with the current script. | 956 /// If [uri] is `null` it is replaced with the current script. |
| 950 static Future<List> spawn(String functionName, String uri, | 957 static Future<List> spawn(String functionName, String uri, List<String> args, |
| 951 List<String> args, message, | 958 message, bool isLight, bool isSpawnUri, bool startPaused) { |
| 952 bool isLight, bool isSpawnUri, bool startPaused) { | |
| 953 // Assume that the compiled version of the Dart file lives just next to the | 959 // Assume that the compiled version of the Dart file lives just next to the |
| 954 // dart file. | 960 // dart file. |
| 955 // TODO(floitsch): support precompiled version of dart2js output. | 961 // TODO(floitsch): support precompiled version of dart2js output. |
| 956 if (uri != null && uri.endsWith(".dart")) uri += ".js"; | 962 if (uri != null && uri.endsWith(".dart")) uri += ".js"; |
| 957 | 963 |
| 958 ReceivePort port = new ReceivePort(); | 964 ReceivePort port = new ReceivePort(); |
| 959 Completer<List> completer = new Completer(); | 965 Completer<List> completer = new Completer(); |
| 960 port.first.then((msg) { | 966 port.first.then((msg) { |
| 961 if (msg[0] == _SPAWNED_SIGNAL) { | 967 if (msg[0] == _SPAWNED_SIGNAL) { |
| 962 completer.complete(msg); | 968 completer.complete(msg); |
| 963 } else { | 969 } else { |
| 964 assert(msg[0] == _SPAWN_FAILED_SIGNAL); | 970 assert(msg[0] == _SPAWN_FAILED_SIGNAL); |
| 965 completer.completeError(msg[1]); | 971 completer.completeError(msg[1]); |
| 966 } | 972 } |
| 967 }); | 973 }); |
| 968 | 974 |
| 969 SendPort signalReply = port.sendPort; | 975 SendPort signalReply = port.sendPort; |
| 970 | 976 |
| 971 if (_globalState.useWorkers && !isLight) { | 977 if (_globalState.useWorkers && !isLight) { |
| 972 _startWorker( | 978 _startWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
| 973 functionName, uri, args, message, isSpawnUri, startPaused, | |
| 974 signalReply, (String message) => completer.completeError(message)); | 979 signalReply, (String message) => completer.completeError(message)); |
| 975 } else { | 980 } else { |
| 976 _startNonWorker( | 981 _startNonWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
| 977 functionName, uri, args, message, isSpawnUri, startPaused, | |
| 978 signalReply); | 982 signalReply); |
| 979 } | 983 } |
| 980 return completer.future; | 984 return completer.future; |
| 981 } | 985 } |
| 982 | 986 |
| 983 static void _startWorker( | 987 static void _startWorker( |
| 984 String functionName, String uri, | 988 String functionName, |
| 985 List<String> args, message, | 989 String uri, |
| 990 List<String> args, |
| 991 message, |
| 986 bool isSpawnUri, | 992 bool isSpawnUri, |
| 987 bool startPaused, | 993 bool startPaused, |
| 988 SendPort replyPort, | 994 SendPort replyPort, |
| 989 void onError(String message)) { | 995 void onError(String message)) { |
| 990 // Make sure that the args list is a fresh generic list. A newly spawned | 996 // Make sure that the args list is a fresh generic list. A newly spawned |
| 991 // isolate should be able to assume that the arguments list is an | 997 // isolate should be able to assume that the arguments list is an |
| 992 // extendable list. | 998 // extendable list. |
| 993 if (args != null) args = new List<String>.from(args); | 999 if (args != null) args = new List<String>.from(args); |
| 994 if (_globalState.isWorker) { | 1000 if (_globalState.isWorker) { |
| 995 _globalState.mainManager.postMessage(_serializeMessage({ | 1001 _globalState.mainManager.postMessage(_serializeMessage({ |
| 996 'command': 'spawn-worker', | 1002 'command': 'spawn-worker', |
| 997 'functionName': functionName, | 1003 'functionName': functionName, |
| 998 'args': args, | 1004 'args': args, |
| 999 'msg': message, | 1005 'msg': message, |
| 1000 'uri': uri, | 1006 'uri': uri, |
| 1001 'isSpawnUri': isSpawnUri, | 1007 'isSpawnUri': isSpawnUri, |
| 1002 'startPaused': startPaused, | 1008 'startPaused': startPaused, |
| 1003 'replyPort': replyPort})); | 1009 'replyPort': replyPort |
| 1010 })); |
| 1004 } else { | 1011 } else { |
| 1005 _spawnWorker(functionName, uri, args, message, | 1012 _spawnWorker(functionName, uri, args, message, isSpawnUri, startPaused, |
| 1006 isSpawnUri, startPaused, replyPort, onError); | 1013 replyPort, onError); |
| 1007 } | 1014 } |
| 1008 } | 1015 } |
| 1009 | 1016 |
| 1010 static void _startNonWorker( | 1017 static void _startNonWorker( |
| 1011 String functionName, String uri, | 1018 String functionName, |
| 1012 List<String> args, var message, | 1019 String uri, |
| 1020 List<String> args, |
| 1021 var message, |
| 1013 bool isSpawnUri, | 1022 bool isSpawnUri, |
| 1014 bool startPaused, | 1023 bool startPaused, |
| 1015 SendPort replyPort) { | 1024 SendPort replyPort) { |
| 1016 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. | 1025 // TODO(eub): support IE9 using an iframe -- Dart issue 1702. |
| 1017 if (uri != null) { | 1026 if (uri != null) { |
| 1018 throw new UnsupportedError( | 1027 throw new UnsupportedError( |
| 1019 "Currently spawnUri is not supported without web workers."); | 1028 "Currently spawnUri is not supported without web workers."); |
| 1020 } | 1029 } |
| 1021 // Clone the message to enforce the restrictions we have on isolate | 1030 // Clone the message to enforce the restrictions we have on isolate |
| 1022 // messages. | 1031 // messages. |
| 1023 message = _clone(message); | 1032 message = _clone(message); |
| 1024 // Make sure that the args list is a fresh generic list. A newly spawned | 1033 // Make sure that the args list is a fresh generic list. A newly spawned |
| 1025 // isolate should be able to assume that the arguments list is an | 1034 // isolate should be able to assume that the arguments list is an |
| 1026 // extendable list. | 1035 // extendable list. |
| 1027 if (args != null) args = new List<String>.from(args); | 1036 if (args != null) args = new List<String>.from(args); |
| 1028 _globalState.topEventLoop.enqueue(new _IsolateContext(), () { | 1037 _globalState.topEventLoop.enqueue(new _IsolateContext(), () { |
| 1029 final func = _getJSFunctionFromName(functionName); | 1038 final func = _getJSFunctionFromName(functionName); |
| 1030 _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort); | 1039 _startIsolate(func, args, message, isSpawnUri, startPaused, replyPort); |
| 1031 }, 'nonworker start'); | 1040 }, 'nonworker start'); |
| 1032 } | 1041 } |
| 1033 | 1042 |
| 1034 static Isolate get currentIsolate { | 1043 static Isolate get currentIsolate { |
| 1035 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); | 1044 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); |
| 1036 return new Isolate(context.controlPort.sendPort, | 1045 return new Isolate(context.controlPort.sendPort, |
| 1037 pauseCapability: context.pauseCapability, | 1046 pauseCapability: context.pauseCapability, |
| 1038 terminateCapability: context.terminateCapability); | 1047 terminateCapability: context.terminateCapability); |
| 1039 } | 1048 } |
| 1040 | 1049 |
| 1041 static void _startIsolate(Function topLevel, | 1050 static void _startIsolate(Function topLevel, List<String> args, message, |
| 1042 List<String> args, message, | 1051 bool isSpawnUri, bool startPaused, SendPort replyTo) { |
| 1043 bool isSpawnUri, | |
| 1044 bool startPaused, | |
| 1045 SendPort replyTo) { | |
| 1046 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); | 1052 _IsolateContext context = JS_CURRENT_ISOLATE_CONTEXT(); |
| 1047 Primitives.initializeStatics(context.id); | 1053 Primitives.initializeStatics(context.id); |
| 1048 // The isolate's port does not keep the isolate open. | 1054 // The isolate's port does not keep the isolate open. |
| 1049 replyTo.send([_SPAWNED_SIGNAL, | 1055 replyTo.send([ |
| 1050 context.controlPort.sendPort, | 1056 _SPAWNED_SIGNAL, |
| 1051 context.pauseCapability, | 1057 context.controlPort.sendPort, |
| 1052 context.terminateCapability]); | 1058 context.pauseCapability, |
| 1059 context.terminateCapability |
| 1060 ]); |
| 1053 | 1061 |
| 1054 void runStartFunction() { | 1062 void runStartFunction() { |
| 1055 context.initialized = true; | 1063 context.initialized = true; |
| 1056 if (!isSpawnUri) { | 1064 if (!isSpawnUri) { |
| 1057 topLevel(message); | 1065 topLevel(message); |
| 1058 } else if (topLevel is _MainFunctionArgsMessage) { | 1066 } else if (topLevel is _MainFunctionArgsMessage) { |
| 1059 topLevel(args, message); | 1067 topLevel(args, message); |
| 1060 } else if (topLevel is _MainFunctionArgs) { | 1068 } else if (topLevel is _MainFunctionArgs) { |
| 1061 topLevel(args); | 1069 topLevel(args); |
| 1062 } else { | 1070 } else { |
| 1063 topLevel(); | 1071 topLevel(); |
| 1064 } | 1072 } |
| 1065 } | 1073 } |
| 1066 | 1074 |
| 1067 if (startPaused) { | 1075 if (startPaused) { |
| 1068 context.addPause(context.pauseCapability, context.pauseCapability); | 1076 context.addPause(context.pauseCapability, context.pauseCapability); |
| 1069 _globalState.topEventLoop.enqueue(context, runStartFunction, | 1077 _globalState.topEventLoop |
| 1070 'start isolate'); | 1078 .enqueue(context, runStartFunction, 'start isolate'); |
| 1071 } else { | 1079 } else { |
| 1072 runStartFunction(); | 1080 runStartFunction(); |
| 1073 } | 1081 } |
| 1074 } | 1082 } |
| 1075 | 1083 |
| 1076 /** | 1084 /** |
| 1077 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor | 1085 * Spawns an isolate in a worker. [factoryName] is the Javascript constructor |
| 1078 * name for the isolate entry point class. | 1086 * name for the isolate entry point class. |
| 1079 */ | 1087 */ |
| 1080 static void _spawnWorker(functionName, String uri, | 1088 static void _spawnWorker( |
| 1081 List<String> args, message, | 1089 functionName, |
| 1082 bool isSpawnUri, | 1090 String uri, |
| 1083 bool startPaused, | 1091 List<String> args, |
| 1084 SendPort replyPort, | 1092 message, |
| 1085 void onError(String message)) { | 1093 bool isSpawnUri, |
| 1094 bool startPaused, |
| 1095 SendPort replyPort, |
| 1096 void onError(String message)) { |
| 1086 if (uri == null) uri = thisScript; | 1097 if (uri == null) uri = thisScript; |
| 1087 final worker = JS('var', 'new Worker(#)', uri); | 1098 final worker = JS('var', 'new Worker(#)', uri); |
| 1088 // Trampolines are used when wanting to call a Dart closure from | 1099 // Trampolines are used when wanting to call a Dart closure from |
| 1089 // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts | 1100 // JavaScript. The helper function DART_CLOSURE_TO_JS only accepts |
| 1090 // top-level or static methods, and the trampoline allows us to capture | 1101 // top-level or static methods, and the trampoline allows us to capture |
| 1091 // arguments and values which can be passed to a static method. | 1102 // arguments and values which can be passed to a static method. |
| 1092 final onerrorTrampoline = JS( | 1103 final onerrorTrampoline = JS( |
| 1093 '', | 1104 '', |
| 1094 ''' | 1105 ''' |
| 1095 (function (f, u, c) { | 1106 (function (f, u, c) { |
| 1096 return function(e) { | 1107 return function(e) { |
| 1097 return f(e, u, c) | 1108 return f(e, u, c) |
| 1098 } | 1109 } |
| 1099 })(#, #, #)''', | 1110 })(#, #, #)''', |
| 1100 workerOnError, uri, onError); | 1111 workerOnError, |
| 1112 uri, |
| 1113 onError); |
| 1101 JS('void', '#.onerror = #', worker, onerrorTrampoline); | 1114 JS('void', '#.onerror = #', worker, onerrorTrampoline); |
| 1102 | 1115 |
| 1103 var processWorkerMessageTrampoline = JS( | 1116 var processWorkerMessageTrampoline = JS( |
| 1104 '', | 1117 '', |
| 1105 """ | 1118 """ |
| 1106 (function (f, a) { | 1119 (function (f, a) { |
| 1107 return function (e) { | 1120 return function (e) { |
| 1108 // We can stop listening for errors when the first message is received as | 1121 // We can stop listening for errors when the first message is received as |
| 1109 // we only listen for messages to determine if the uri was bad. | 1122 // we only listen for messages to determine if the uri was bad. |
| 1110 e.onerror = null; | 1123 e.onerror = null; |
| 1111 return f(a, e); | 1124 return f(a, e); |
| 1112 } | 1125 } |
| 1113 })(#, #)""", | 1126 })(#, #)""", |
| 1114 _processWorkerMessage, | 1127 _processWorkerMessage, |
| 1115 worker); | 1128 worker); |
| 1116 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); | 1129 JS('void', '#.onmessage = #', worker, processWorkerMessageTrampoline); |
| 1117 var workerId = _globalState.nextManagerId++; | 1130 var workerId = _globalState.nextManagerId++; |
| 1118 // We also store the id on the worker itself so that we can unregister it. | 1131 // We also store the id on the worker itself so that we can unregister it. |
| 1119 workerIds[worker] = workerId; | 1132 workerIds[worker] = workerId; |
| 1120 _globalState.managers[workerId] = worker; | 1133 _globalState.managers[workerId] = worker; |
| 1121 JS('void', '#.postMessage(#)', worker, _serializeMessage({ | 1134 JS( |
| 1122 'command': 'start', | 1135 'void', |
| 1123 'id': workerId, | 1136 '#.postMessage(#)', |
| 1124 // Note: we serialize replyPort twice because the child worker needs to | 1137 worker, |
| 1125 // first deserialize the worker id, before it can correctly deserialize | 1138 _serializeMessage({ |
| 1126 // the port (port deserialization is sensitive to what is the current | 1139 'command': 'start', |
| 1127 // workerId). | 1140 'id': workerId, |
| 1128 'replyTo': _serializeMessage(replyPort), | 1141 // Note: we serialize replyPort twice because the child worker needs t
o |
| 1129 'args': args, | 1142 // first deserialize the worker id, before it can correctly deserializ
e |
| 1130 'msg': _serializeMessage(message), | 1143 // the port (port deserialization is sensitive to what is the current |
| 1131 'isSpawnUri': isSpawnUri, | 1144 // workerId). |
| 1132 'startPaused': startPaused, | 1145 'replyTo': _serializeMessage(replyPort), |
| 1133 'functionName': functionName })); | 1146 'args': args, |
| 1147 'msg': _serializeMessage(message), |
| 1148 'isSpawnUri': isSpawnUri, |
| 1149 'startPaused': startPaused, |
| 1150 'functionName': functionName |
| 1151 })); |
| 1134 } | 1152 } |
| 1135 | 1153 |
| 1136 static bool workerOnError( | 1154 static bool workerOnError( |
| 1137 /* Event */ event, | 1155 /* Event */ event, |
| 1138 String uri, | 1156 String uri, |
| 1139 void onError(String message)) { | 1157 void onError(String message)) { |
| 1140 // Attempt to shut up the browser, as the error has been handled. Chrome | 1158 // Attempt to shut up the browser, as the error has been handled. Chrome |
| 1141 // ignores this :-( | 1159 // ignores this :-( |
| 1142 JS('void', '#.preventDefault()', event); | 1160 JS('void', '#.preventDefault()', event); |
| 1143 String message = JS('String|Null', '#.message', event); | 1161 String message = JS('String|Null', '#.message', event); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1158 ********************************************************/ | 1176 ********************************************************/ |
| 1159 | 1177 |
| 1160 /** Common functionality to all send ports. */ | 1178 /** Common functionality to all send ports. */ |
| 1161 abstract class _BaseSendPort implements SendPort { | 1179 abstract class _BaseSendPort implements SendPort { |
| 1162 /** Id for the destination isolate. */ | 1180 /** Id for the destination isolate. */ |
| 1163 final int _isolateId; | 1181 final int _isolateId; |
| 1164 | 1182 |
| 1165 const _BaseSendPort(this._isolateId); | 1183 const _BaseSendPort(this._isolateId); |
| 1166 | 1184 |
| 1167 void _checkReplyTo(SendPort replyTo) { | 1185 void _checkReplyTo(SendPort replyTo) { |
| 1168 if (replyTo != null | 1186 if (replyTo != null && |
| 1169 && replyTo is! _NativeJsSendPort | 1187 replyTo is! _NativeJsSendPort && |
| 1170 && replyTo is! _WorkerSendPort) { | 1188 replyTo is! _WorkerSendPort) { |
| 1171 throw new Exception("SendPort.send: Illegal replyTo port type"); | 1189 throw new Exception("SendPort.send: Illegal replyTo port type"); |
| 1172 } | 1190 } |
| 1173 } | 1191 } |
| 1174 | 1192 |
| 1175 void send(var message); | 1193 void send(var message); |
| 1176 bool operator ==(var other); | 1194 bool operator ==(var other); |
| 1177 int get hashCode; | 1195 int get hashCode; |
| 1178 } | 1196 } |
| 1179 | 1197 |
| 1180 /** A send port that delivers messages in-memory via native JavaScript calls. */ | 1198 /** A send port that delivers messages in-memory via native JavaScript calls. */ |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1195 isolate.handleControlMessage(msg); | 1213 isolate.handleControlMessage(msg); |
| 1196 return; | 1214 return; |
| 1197 } | 1215 } |
| 1198 _globalState.topEventLoop.enqueue(isolate, () { | 1216 _globalState.topEventLoop.enqueue(isolate, () { |
| 1199 if (!_receivePort._isClosed) { | 1217 if (!_receivePort._isClosed) { |
| 1200 _receivePort._add(msg); | 1218 _receivePort._add(msg); |
| 1201 } | 1219 } |
| 1202 }, 'receive $message'); | 1220 }, 'receive $message'); |
| 1203 } | 1221 } |
| 1204 | 1222 |
| 1205 bool operator ==(var other) => (other is _NativeJsSendPort) && | 1223 bool operator ==(var other) => |
| 1206 (_receivePort == other._receivePort); | 1224 (other is _NativeJsSendPort) && (_receivePort == other._receivePort); |
| 1207 | 1225 |
| 1208 int get hashCode => _receivePort._id; | 1226 int get hashCode => _receivePort._id; |
| 1209 } | 1227 } |
| 1210 | 1228 |
| 1211 /** A send port that delivers messages via worker.postMessage. */ | 1229 /** A send port that delivers messages via worker.postMessage. */ |
| 1212 // TODO(eub): abstract this for iframes. | 1230 // TODO(eub): abstract this for iframes. |
| 1213 class _WorkerSendPort extends _BaseSendPort implements SendPort { | 1231 class _WorkerSendPort extends _BaseSendPort implements SendPort { |
| 1214 final int _workerId; | 1232 final int _workerId; |
| 1215 final int _receivePortId; | 1233 final int _receivePortId; |
| 1216 | 1234 |
| 1217 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) | 1235 const _WorkerSendPort(this._workerId, int isolateId, this._receivePortId) |
| 1218 : super(isolateId); | 1236 : super(isolateId); |
| 1219 | 1237 |
| 1220 void send(var message) { | 1238 void send(var message) { |
| 1221 final workerMessage = _serializeMessage({ | 1239 final workerMessage = |
| 1222 'command': 'message', | 1240 _serializeMessage({'command': 'message', 'port': this, 'msg': message}); |
| 1223 'port': this, | |
| 1224 'msg': message}); | |
| 1225 | 1241 |
| 1226 if (_globalState.isWorker) { | 1242 if (_globalState.isWorker) { |
| 1227 // Communication from one worker to another go through the | 1243 // Communication from one worker to another go through the |
| 1228 // main worker. | 1244 // main worker. |
| 1229 _globalState.mainManager.postMessage(workerMessage); | 1245 _globalState.mainManager.postMessage(workerMessage); |
| 1230 } else { | 1246 } else { |
| 1231 // Deliver the message only if the worker is still alive. | 1247 // Deliver the message only if the worker is still alive. |
| 1232 /* Worker */ var manager = _globalState.managers[_workerId]; | 1248 /* Worker */ var manager = _globalState.managers[_workerId]; |
| 1233 if (manager != null) { | 1249 if (manager != null) { |
| 1234 JS('void', '#.postMessage(#)', manager, workerMessage); | 1250 JS('void', '#.postMessage(#)', manager, workerMessage); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1260 _globalState.currentContext.register(_id, this); | 1276 _globalState.currentContext.register(_id, this); |
| 1261 } | 1277 } |
| 1262 | 1278 |
| 1263 RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ { | 1279 RawReceivePortImpl.weak(this._handler) : _id = _nextFreeId++ { |
| 1264 _globalState.currentContext.registerWeak(_id, this); | 1280 _globalState.currentContext.registerWeak(_id, this); |
| 1265 } | 1281 } |
| 1266 | 1282 |
| 1267 // Creates the control port of an isolate. | 1283 // Creates the control port of an isolate. |
| 1268 // This is created before the isolate context object itself, | 1284 // This is created before the isolate context object itself, |
| 1269 // so it cannot access the static _nextFreeId field. | 1285 // so it cannot access the static _nextFreeId field. |
| 1270 RawReceivePortImpl._controlPort() : _handler = null, _id = 0; | 1286 RawReceivePortImpl._controlPort() |
| 1287 : _handler = null, |
| 1288 _id = 0; |
| 1271 | 1289 |
| 1272 void set handler(Function newHandler) { | 1290 void set handler(Function newHandler) { |
| 1273 _handler = newHandler; | 1291 _handler = newHandler; |
| 1274 } | 1292 } |
| 1275 | 1293 |
| 1276 // Close the port without unregistering it. | 1294 // Close the port without unregistering it. |
| 1277 // Used by an isolate context to close all ports when shutting down. | 1295 // Used by an isolate context to close all ports when shutting down. |
| 1278 void _close() { | 1296 void _close() { |
| 1279 _isClosed = true; | 1297 _isClosed = true; |
| 1280 _handler = null; | 1298 _handler = null; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1305 | 1323 |
| 1306 ReceivePortImpl.weak() | 1324 ReceivePortImpl.weak() |
| 1307 : this.fromRawReceivePort(new RawReceivePortImpl.weak(null)); | 1325 : this.fromRawReceivePort(new RawReceivePortImpl.weak(null)); |
| 1308 | 1326 |
| 1309 ReceivePortImpl.fromRawReceivePort(this._rawPort) { | 1327 ReceivePortImpl.fromRawReceivePort(this._rawPort) { |
| 1310 _controller = new StreamController(onCancel: close, sync: true); | 1328 _controller = new StreamController(onCancel: close, sync: true); |
| 1311 _rawPort.handler = _controller.add; | 1329 _rawPort.handler = _controller.add; |
| 1312 } | 1330 } |
| 1313 | 1331 |
| 1314 StreamSubscription listen(void onData(var event), | 1332 StreamSubscription listen(void onData(var event), |
| 1315 {Function onError, | 1333 {Function onError, void onDone(), bool cancelOnError}) { |
| 1316 void onDone(), | 1334 return _controller.stream.listen(onData, |
| 1317 bool cancelOnError}) { | 1335 onError: onError, onDone: onDone, cancelOnError: cancelOnError); |
| 1318 return _controller.stream.listen(onData, onError: onError, onDone: onDone, | |
| 1319 cancelOnError: cancelOnError); | |
| 1320 } | 1336 } |
| 1321 | 1337 |
| 1322 void close() { | 1338 void close() { |
| 1323 _rawPort.close(); | 1339 _rawPort.close(); |
| 1324 _controller.close(); | 1340 _controller.close(); |
| 1325 } | 1341 } |
| 1326 | 1342 |
| 1327 SendPort get sendPort => _rawPort.sendPort; | 1343 SendPort get sendPort => _rawPort.sendPort; |
| 1328 } | 1344 } |
| 1329 | 1345 |
| 1330 class TimerImpl implements Timer { | 1346 class TimerImpl implements Timer { |
| 1331 final bool _once; | 1347 final bool _once; |
| 1332 bool _inEventLoop = false; | 1348 bool _inEventLoop = false; |
| 1333 int _handle; | 1349 int _handle; |
| 1334 | 1350 |
| 1335 TimerImpl(int milliseconds, void callback()) | 1351 TimerImpl(int milliseconds, void callback()) : _once = true { |
| 1336 : _once = true { | |
| 1337 if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { | 1352 if (milliseconds == 0 && (!hasTimer() || _globalState.isWorker)) { |
| 1338 | |
| 1339 void internalCallback() { | 1353 void internalCallback() { |
| 1340 _handle = null; | 1354 _handle = null; |
| 1341 callback(); | 1355 callback(); |
| 1342 } | 1356 } |
| 1343 | 1357 |
| 1344 // Setting _handle to something different from null indicates that the | 1358 // Setting _handle to something different from null indicates that the |
| 1345 // callback has not been run. Hence, the choice of 1 is arbitrary. | 1359 // callback has not been run. Hence, the choice of 1 is arbitrary. |
| 1346 _handle = 1; | 1360 _handle = 1; |
| 1347 | 1361 |
| 1348 // This makes a dependency between the async library and the | 1362 // This makes a dependency between the async library and the |
| 1349 // event loop of the isolate library. The compiler makes sure | 1363 // event loop of the isolate library. The compiler makes sure |
| 1350 // that the event loop is compiled if [Timer] is used. | 1364 // that the event loop is compiled if [Timer] is used. |
| 1351 // TODO(7907): In case of web workers, we need to use the event | 1365 // TODO(7907): In case of web workers, we need to use the event |
| 1352 // loop instead of setTimeout, to make sure the futures get executed in | 1366 // loop instead of setTimeout, to make sure the futures get executed in |
| 1353 // order. | 1367 // order. |
| 1354 _globalState.topEventLoop.enqueue( | 1368 _globalState.topEventLoop |
| 1355 _globalState.currentContext, internalCallback, 'timer'); | 1369 .enqueue(_globalState.currentContext, internalCallback, 'timer'); |
| 1356 _inEventLoop = true; | 1370 _inEventLoop = true; |
| 1357 } else if (hasTimer()) { | 1371 } else if (hasTimer()) { |
| 1358 | |
| 1359 void internalCallback() { | 1372 void internalCallback() { |
| 1360 _handle = null; | 1373 _handle = null; |
| 1361 leaveJsAsync(); | 1374 leaveJsAsync(); |
| 1362 callback(); | 1375 callback(); |
| 1363 } | 1376 } |
| 1364 | 1377 |
| 1365 enterJsAsync(); | 1378 enterJsAsync(); |
| 1366 | 1379 |
| 1367 _handle = JS( | 1380 _handle = JS( |
| 1368 'int', '#.setTimeout(#, #)', global, internalCallback, milliseconds); | 1381 'int', '#.setTimeout(#, #)', global, internalCallback, milliseconds); |
| 1369 } else { | 1382 } else { |
| 1370 assert(milliseconds > 0); | 1383 assert(milliseconds > 0); |
| 1371 throw new UnsupportedError("Timer greater than 0."); | 1384 throw new UnsupportedError("Timer greater than 0."); |
| 1372 } | 1385 } |
| 1373 } | 1386 } |
| 1374 | 1387 |
| 1375 TimerImpl.periodic(int milliseconds, void callback(Timer timer)) | 1388 TimerImpl.periodic(int milliseconds, void callback(Timer timer)) |
| 1376 : _once = false { | 1389 : _once = false { |
| 1377 if (hasTimer()) { | 1390 if (hasTimer()) { |
| 1378 enterJsAsync(); | 1391 enterJsAsync(); |
| 1379 _handle = JS('int', '#.setInterval(#, #)', | 1392 _handle = JS('int', '#.setInterval(#, #)', global, () { |
| 1380 global, () { callback(this); }, milliseconds); | 1393 callback(this); |
| 1394 }, milliseconds); |
| 1381 } else { | 1395 } else { |
| 1382 throw new UnsupportedError("Periodic timer."); | 1396 throw new UnsupportedError("Periodic timer."); |
| 1383 } | 1397 } |
| 1384 } | 1398 } |
| 1385 | 1399 |
| 1386 void cancel() { | 1400 void cancel() { |
| 1387 if (hasTimer()) { | 1401 if (hasTimer()) { |
| 1388 if (_inEventLoop) { | 1402 if (_inEventLoop) { |
| 1389 throw new UnsupportedError("Timer in event loop cannot be canceled."); | 1403 throw new UnsupportedError("Timer in event loop cannot be canceled."); |
| 1390 } | 1404 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1401 } | 1415 } |
| 1402 } | 1416 } |
| 1403 | 1417 |
| 1404 bool get isActive => _handle != null; | 1418 bool get isActive => _handle != null; |
| 1405 } | 1419 } |
| 1406 | 1420 |
| 1407 bool hasTimer() { | 1421 bool hasTimer() { |
| 1408 return JS('', '#.setTimeout', global) != null; | 1422 return JS('', '#.setTimeout', global) != null; |
| 1409 } | 1423 } |
| 1410 | 1424 |
| 1411 | |
| 1412 /** | 1425 /** |
| 1413 * Implementation class for [Capability]. | 1426 * Implementation class for [Capability]. |
| 1414 * | 1427 * |
| 1415 * It has the same name to make it harder for users to distinguish. | 1428 * It has the same name to make it harder for users to distinguish. |
| 1416 */ | 1429 */ |
| 1417 class CapabilityImpl implements Capability { | 1430 class CapabilityImpl implements Capability { |
| 1418 /** Internal random secret identifying the capability. */ | 1431 /** Internal random secret identifying the capability. */ |
| 1419 final int _id; | 1432 final int _id; |
| 1420 | 1433 |
| 1421 CapabilityImpl() : this._internal(random64()); | 1434 CapabilityImpl() : this._internal(random64()); |
| 1422 | 1435 |
| 1423 CapabilityImpl._internal(this._id); | 1436 CapabilityImpl._internal(this._id); |
| 1424 | 1437 |
| 1425 int get hashCode { | 1438 int get hashCode { |
| 1426 // Thomas Wang 32 bit Mix. | 1439 // Thomas Wang 32 bit Mix. |
| 1427 // http://www.concentric.net/~Ttwang/tech/inthash.htm | 1440 // http://www.concentric.net/~Ttwang/tech/inthash.htm |
| 1428 // (via https://gist.github.com/badboy/6267743) | 1441 // (via https://gist.github.com/badboy/6267743) |
| 1429 int hash = _id; | 1442 int hash = _id; |
| 1430 hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64. | 1443 hash = (hash >> 0) ^ (hash ~/ 0x100000000); // To 32 bit from ~64. |
| 1431 hash = (~hash + (hash << 15)) & 0xFFFFFFFF; | 1444 hash = (~hash + (hash << 15)) & 0xFFFFFFFF; |
| 1432 hash ^= hash >> 12; | 1445 hash ^= hash >> 12; |
| 1433 hash = (hash * 5) & 0xFFFFFFFF; | 1446 hash = (hash * 5) & 0xFFFFFFFF; |
| 1434 hash ^= hash >> 4; | 1447 hash ^= hash >> 4; |
| 1435 hash = (hash * 2057) & 0xFFFFFFFF; | 1448 hash = (hash * 2057) & 0xFFFFFFFF; |
| 1436 hash ^= hash >> 16; | 1449 hash ^= hash >> 16; |
| 1437 return hash; | 1450 return hash; |
| 1438 } | 1451 } |
| 1439 | 1452 |
| 1440 bool operator==(Object other) { | 1453 bool operator ==(Object other) { |
| 1441 if (identical(other, this)) return true; | 1454 if (identical(other, this)) return true; |
| 1442 if (other is CapabilityImpl) { | 1455 if (other is CapabilityImpl) { |
| 1443 return identical(_id, other._id); | 1456 return identical(_id, other._id); |
| 1444 } | 1457 } |
| 1445 return false; | 1458 return false; |
| 1446 } | 1459 } |
| 1447 } | 1460 } |
| OLD | NEW |